区间dp专题

区间dp专题

基本思想

区间dp一类的问题往往子问题具有很明显的区间性质,也就是说我们可以通过将子问题定义为整个区间的一个子区间.因为一个大区间可以切分成两段相邻的子区间.从这点出发,我们便可以找到递推关系.

1.纸牌游戏

蜘蛛牌游戏规则是这样的:只能将牌拖到比它大一的牌上面(AAA最小,KKK最大),如果拖动的牌上有按顺序排好的牌时,那么这些牌也跟着一起移动,游戏的目的是将所有的牌按同一花色从小到大排好。为了简单起见,我们的游戏只有同一花色的牌,但是这样XCX又觉得太简单了,于是他把牌数增加到了n(1&lt;=n&lt;=100)n(1&lt;=n&lt;=100)n(1<=n<=100),牌随机的在一行上展开,编号从1到nnn,把第iii号上的牌移到第j号牌上,移动距离为abs(i−j)abs(i-j)abs(ij),现在你要做的是求出完成游戏的最小移动距离。

题解

采用区间动态规划的方式,但是直接进行区间DP是没有任何意义的, 所以实际上我们需要另寻状态的定义方式.

我们需要对数列进行变化一下,我们进行dp的区间[a,b][a,b][a,b]定义为高度为aaa到高度为bbb的纸牌叠加到一起,所需要的最少距离和。

在一开始,我们定义数组arr[x]arr[x]arr[x]中存储的是高度为xxx的纸牌所在的位置。那么状态转移就可以写成:dp[a][b]=dp[a][j]+dp[j+1][b]+abs(arr[b]−arr[j])dp[a][b] = dp[a][j] + dp[j+1][b] + abs(arr[b]-arr[j])dp[a][b]=dp[a][j]+dp[j+1][b]+abs(arr[b]arr[j])

这样的话,问题就迎刃而解了,所以说如何去定义问题是dpdpdp问题的一大难点和关键点.

代码

#include<bits/stdc++.h>
using namespace std;int a[105],n;
int dp[105][105];int main()
{int T;scanf("%d",&T);while(T--){scanf("%d",&n);for(int i=1;i<=n;i++){int x;scanf("%d",&x);a[x]=i;}for(int k=2;k<=n;k++){for(int i=1;i<=n-k+1;i++){int l=i,r=i+k-1;dp[l][r]=1e9+7;for(int j=l;j<=r-1;j++)dp[l][r]=min(dp[l][r],dp[l][j]+dp[j+1][r]+abs(a[r]-a[j]));}}printf("%d\n",dp[1][n]);}return 0;
}

2.Multiplication Puzzle POJ - 1651

在一个序列中,拿走一个数字,那么得分就是这个数字以及它相邻的两个数字,这三个数字的乘积, 求最小得分。

题解

这道题乍一看感觉是区间DP,但是需要逆向思考的技巧。

dp[i][k]dp[i][k]dp[i][k]表示以iii开头的,长度kkk的区间。

我们考虑一个区间的时候,记录区间的两个端点分别为l,rl,rl,r

这个区间两侧的端点是不能被拿走的,那么我们考虑最后一个被拿走的数字kkk,它的得分一定是区间端点的两个数和它的乘积(a[l]∗a[k]∗a[r]a[l]*a[k]*a[r]a[l]a[k]a[r])。

然后我们考虑区间[l,k][l,k][l,k]之间的情况,这个区间被拿的只剩下区间两个端点了,所以可以直接用子结构dp[l][k−l+1]dp[l][k-l+1]dp[l][kl+1]

同理区间p[k,r]p[k,r]p[k,r]也被拿的只剩下区间的两个端点了,直接用子结构dp[k][r−l−k+1]dp[k][r-l-k+1]dp[k][rlk+1]

这样的话递推式就非常的清晰了。

dp[i][k]=min(dp[i][k],dp[i][j+1]+dp[i+j][k−j]+a[i]∗a[i+j]∗a[i+k−1])dp[i][k] = min(dp[i][k],dp[i][j+1] + dp[i+j][k-j] + a[i]*a[i+j]*a[i+k-1])dp[i][k]=min(dp[i][k],dp[i][j+1]+dp[i+j][kj]+a[i]a[i+j]a[i+k1])

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX = 106;
int dp[MAX][MAX];
int a[MAX];
int n;
int main(){scanf("%d",&n);for(int i = 0;i < n;i++){cin>>a[i];}for(int k = 3;k <= n;k++){for(int i = 0 ;i + k <= n;i++){dp[i][k] = 1e9;for(int j = 1;j < k-1;j++){dp[i][k] = min(dp[i][k],dp[i][j+1] + dp[i+j][k-j] + a[i]*a[i+j]*a[i+k-1]);}}}cout<<dp[0][n]<<endl;
}

3.括号匹配问题 Brackets POJ - 2955

给定一个括号序列,求最长的合法括号表达式子序列.

题解

再明显不过的区间DP的题目了,要求求出给出符号式中最大匹配的括号数。
考虑区间[l,r][l,r][l,r],如果str[l]str[l]str[l]str[r]str[r]str[r]匹配了,那么转移方程为:

dp[l][r]=max(dp[l][r],dp[l+1][r−1]+2)dp[l][r] = max(dp[l][r],dp[l+1][r-1]+ 2)dp[l][r]=max(dp[l][r],dp[l+1][r1]+2)

还有一种情况就是考虑将区间分成2部分

dp[l][r]=max(dp[l][r],dp[l][k]+dp[k+1][r])dp[l][r] = max(dp[l][r],dp[l][k]+dp[k+1][r])dp[l][r]=max(dp[l][r],dp[l][k]+dp[k+1][r])

然后就成了,没错就这么简单

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAX = 105;
char str[MAX];
int dp[MAX][MAX];
int mp[128];
int main(){mp['('] = ')';mp['['] = ']';while(gets(str)){memset(dp,0,sizeof(dp));if(str[0] == 'e'){break;}int n = strlen(str);for(int k = 2;k <= n;k++){for(int i = 0;i + k <= n;i++){//左闭右开 for(int j = i + 1;j < i + k;j++){dp[i][i+k] = max(dp[i][i + k],dp[i][j] + dp[j][i+k]);if(mp[str[i]] == str[i+k-1])dp[i][i+k] = max(dp[i][i+k],dp[i+1][i+k-1]+2);}} }printf("%d\n",dp[0][n]);}return 0;
}

4.You Are the One HDU - 4283

题目大意就是说给定一个有序序列和一个栈,对于队伍前头的一个人,有两个操作,一个是直接迈向舞台,另一个操作就是迈入栈中。在一个时间下,迈向舞台的人可以是原始队伍里的人,也可以是栈里面的人。举例来说,假定队伍此时为 队首=>4 5<=队尾,栈此时为 栈顶=>3 2 1 <=栈底,那么下一个时刻可能是4进入栈中,或者4进入舞台,或者3从栈中去向舞台。

nnn个人参加非诚勿扰,每个人都有nin_ini的屌丝值,如果前面有kkk个人比他早,他就会有(k−1)∗(ni)(k−1)*(n_i)(k1)(ni)的不开心值,你可以让一些人进入一个小黑屋,来改变上场顺序,但是小黑屋是类似栈,先入后出。求最小不开心值.

题解

我们考虑如下事实:

考虑区间[l,r][l,r][l,r],区间长度k=r−l+1k = r-l+1k=rl+1

如果区间的第一个元素可能是第111 一直到第 kkk个进入舞台的,假设第一个元素是第ppp个进入舞台的1&lt;=p&lt;=k1&lt;=p&lt;=k1<=p<=k,那么我们知道第一个元素一定要先入栈,区间[l+1,l+k−1][l+1,l+k-1][l+1,l+k1]中的元素一定会先第一个元素而进入舞台,这样的话,就转化到了子结构dp[l+1][l+k−1]dp[l+1][l+k-1]dp[l+1][l+k1]中去了。

同时[l+k,r][l+k,r][l+k,r]中的元素一定后于第一个元素而进入舞台。他们共同要等待的耗费为p∗sum[l+k,r]p*sum[l+k,r]psum[l+k,r],另外还有一部分耗费为dp[l+k][r]dp[l+k][r]dp[l+k][r]

综上所述,得到转移方程

dp[l][r]=min(dp[l][r],dp[l+1][l+k−1]+(k−1)∗a[l+p−1]+p∗sum[l+k,r]+dp[l+k][r])dp[l][r] = min(dp[l][r],dp[l+1][l+k-1] + (k-1)*a[l+p-1] + p*sum[l+k,r] + dp[l+k][r])dp[l][r]=min(dp[l][r],dp[l+1][l+k1]+(k1)a[l+p1]+psum[l+k,r]+dp[l+k][r])

注意:代码中用的区间方式为左闭右开。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAX = 106;
char strA[MAX];
char strB[MAX]; 
int dp[MAX][MAX];
int ans[MAX];
int n;
int main(){while(scanf("%s %s",strA,strB) != EOF){memset(dp,0,sizeof(dp));memset(ans,0,sizeof(ans));n = strlen(strA);for(int i = 0;i < n;i++) dp[i][i+1] = 1;for(int len = 2;len <= n;len++){for(int i = 0;i + len <= n;i++){dp[i][i+len] = dp[i+1][i+len] + (strB[i] == strB[i+len-1]?0:1);for(int j = i+1;j < i+len;j++){dp[i][i+len] = min(dp[i][i+len],dp[i][j]+dp[j][i+len]);} }}ans[0] = strA[0] == strB[0] ? 0:1;for(int i = 1;i < n;i++){ans[i] = dp[0][i+1];if(strB[i] == strA[i])ans[i] = min(ans[i],ans[i-1]);for(int j = 0;j < i;j++){ans[i] = min(ans[i],ans[j] + dp[j+1][i+1]);}}cout<<ans[n-1]<<endl;}return 0;
}

5.Coloring Brackets CodeForces - 149D

题目大意:

给定合法的括号序列,让你给括弧上色,并且上色时一定要满足3个要求:
(1)每个括号要么被上红色,要么被上蓝色,要么不上色。
(2)一对匹配的左右括弧,有且只有其中的一个可以被上色。
(3)相邻的括弧不能被涂上相同的颜色。

题解

首先我们要进行预处理,求出每个括号的唯一配对的括号,即寻找他们一一对应的关系,这个预处理很简单,用栈操作一下就可以了

	gets(str);stack<int> stk;int len = strlen(str);for(int i = 0;i < len;i++){if(str[i] == '('){stk.push(i);}else{int p = stk.top();stk.pop();mp[p] = i;mp[i] = p;}}

下面我们考虑的区间,全部都是配对合法的区间!

用一个四维数组dp[l][r][i][j]表示区间[l,r]且l处被涂上i色,r处被涂上j色。(规定无色为0,红色为1,蓝色为2)

那么我们可以得到下面的状态转移方程:

(1)当区间长度只有2时候(两个括弧一定是配对的,因为我们考虑的所有区间都是配对合法的区间),上色方案是非常好确定的。

dp[l][r][0][1]=1;dp[l][r][0][1] = 1;dp[l][r][0][1]=1;
dp[l][r][0][2]=1;dp[l][r][0][2] = 1;dp[l][r][0][2]=1;
dp[l][r][1][0]=1;dp[l][r][1][0] = 1;dp[l][r][1][0]=1;
dp[l][r][2][0]=1;dp[l][r][2][0] = 1;dp[l][r][2][0]=1;

(2)当区间的左右括弧是配对的时候(判断左右括弧是否匹配,用到了预处理得到的结果)。则有如下转移方法:

if(j != 1)
dp[l][r][0][1] = (dp[l][r][0][1] + dp[l+1][r-1][i][j])%MOD;
if(j != 2)
dp[l][r][0][2] = (dp[l][r][0][2] + dp[l+1][r-1][i][j])%MOD;
if(i != 1)
dp[l][r][1][0] = (dp[l][r][1][0] + dp[l+1][r-1][i][j])%MOD; 
if(i != 2)
dp[l][r][2][0] = (dp[l][r][2][0] + dp[l+1][r-1][i][j])%MOD; 

(3)当区间非左右配对时,把区间划分为左右两个各自合法 的区间,转移方程是。

dp[l][r][i][j]=(dp[l][r][i][j]+dp[l][k][i][p]∗dp[k+1][r][q][j]%MOD)%MOD;dp[l][r][i][j] = (dp[l][r][i][j] + dp[l][k][i][p] * dp[k+1][r][q][j]\%MOD) \% MOD;dp[l][r][i][j]=(dp[l][r][i][j]+dp[l][k][i][p]dp[k+1][r][q][j]%MOD)%MOD;

这里注意kkk代表的是与l配对的括号,那么k+1k+1k+1就是与rrr配对的括号了。(这就是预处理的作用!)

而在动态规划的实现过程中,我们发现并非所有的区间都是合法的,只有少部分的区间是合法的,因此我们用自顶向下的记忆化dp的方法,这样可以简化代码的实现。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
#define int long long 
using namespace std;
const int MAX = 705;
const int MOD = 1e9 + 7;
int used[MAX][MAX];
int mp[MAX];
char str[MAX];
int dp[MAX][MAX][3][3];
void dfs(int l,int r){if(used[l][r]) return ;used[l][r] = 1;if(l + 1 == r){dp[l][r][0][1] = 1;dp[l][r][0][2] = 1;dp[l][r][1][0] = 1;dp[l][r][2][0] = 1;return ;}if(mp[l] == r){//配对的情况 dfs(l+1,r-1);for(int i = 0;i < 3;i++){for(int j = 0;j < 3;j++){if(j != 1)dp[l][r][0][1] = (dp[l][r][0][1] + dp[l+1][r-1][i][j])%MOD;if(j != 2)dp[l][r][0][2] = (dp[l][r][0][2] + dp[l+1][r-1][i][j])%MOD;if(i != 1)dp[l][r][1][0] = (dp[l][r][1][0] + dp[l+1][r-1][i][j])%MOD;	if(i != 2)dp[l][r][2][0] = (dp[l][r][2][0] + dp[l+1][r-1][i][j])%MOD;	}}}else{int k = mp[l];dfs(l,k);dfs(k+1,r);for(int i = 0;i < 3;i++){for(int j = 0;j < 3;j++){for(int p = 0;p < 3;p++){for(int q = 0;q < 3;q++){if(p + q == 0 || p != q ){dp[l][r][i][j] = (dp[l][r][i][j] + dp[l][k][i][p] * dp[k+1][r][q][j]%MOD)%MOD;}}} }} }
}
main(){gets(str);stack<int> stk;int len = strlen(str);for(int i = 0;i < len;i++){if(str[i] == '('){stk.push(i);}else{int p = stk.top();stk.pop();mp[p] = i;mp[i] = p;}}dfs(0,len-1);int ans = 0;for(int i = 0;i < 3;i++){for(int j = 0;j < 3;j++){ans = (ans + dp[0][len-1][i][j])%MOD;}}cout<<ans<<endl;return 0;
} 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/320927.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.Net Core开发日志——Global Tools

.Net Core 2.1引入了一个新的功能&#xff0c;Global Tools&#xff0c;其本质是包含控制台应用程序的nuget包&#xff0c;目前而言&#xff0c;还没有特别有用的工具&#xff0c;不过相信随着时间的推移&#xff0c;各种有创意或者实用性强的Global Tools会出现在大家的视野里…

【DP】回文词 (ssl 1813)

回文词 ssl 1813 题目大意&#xff1a; 给出一个式子&#xff0c;最少要加多少个字符才能让这个式子是一个“回文词” 原题&#xff1a; 题目描述 回文词是一种对称的字符串,也就是说:一个回文词,从左向右读和从右向左读的结果都是一样的.任意给定一个字符串,通过插入若干…

POJ3678-Katu Puzzle【2-SAT】

正题 题目链接:http://poj.org/problem?id3678 题目大意 nnn个xix_ixi​为0/10/10/1。有mmm个条件表示xiandxjax_i\ and\ x_jaxi​ and xj​a或xiorxjax_i\ or\ x_jaxi​ or xj​a或xixorxjax_i\ xor\ x_jaxi​ xor xj​a。 求构造一组合法的xix_ixi​。 解题思路 讨论一下 …

Simple-Faster-RCNN源码学习笔记

Simple-Faster-RCNN 源码学习 项目github地址: https://github.com/chenyuntc/simple-faster-rcnn-pytorch 源码 源文件: model/utils/bbox_tools.py 方法: loc2bbox(src_bbox, loc) 参数含义: src_bbox描述的是bbox的坐标.loc表示的偏移(offsets)和缩放尺度(scales). 给…

API网关模式

什么是网关网关一词来源于计算机网络中的定义&#xff0c;网关(Gateway)又称网间连接器、协议转换器。网关的准确定义是&#xff1a; 两个计算机程序或系统之间的连接&#xff0c;网关作为两个程序之间的门户&#xff0c;允许它们通过不同计算机之间的协议通信来共享信息。顾名…

楼层

楼层 题目大意&#xff1a; 有两个数m和t&#xff0c;问1~m之间去掉有数字t的数之后还有多少个数 原题&#xff1a; 题目描述 mxy 感觉新世界的大门打开了。 ta 决定要在新世界的旅馆中找间房住。已知新世界每天都有一个高能的数字 t&#xff0c;这个数字在楼层中是不会出…

P3825-[NOI2017]游戏【2-SAT】

正题 题目链接:https://www.luogu.com.cn/problem/P3825 题目大意 nnn场比赛&#xff0c;对于场地aaa不能用赛车AAA&#xff08;b,cb,cb,c以此类推&#xff09;&#xff0c;对于场地xxx可以用任何赛车。然后给定mmm条条件形如iIjJi\ I\ j\ Ji I j J表示在第iii场比赛使用赛车I…

CVPR19 基于图卷积网络的多标签图像识别模型 论文笔记

笔记 旷视研究院的研究员提出了如下模型,用于图像的多标签分类. 该模型与一般模型不一样的一点是,它的分类器是生成的,因此它有一个专门生成分类器的子网络. 网络主要由两部分构成 特征表示子网络,该网络由ResNet-101构成,即蓝色框圈出的部分.分类器生成子网络,该网络由3个…

日行千里,全凭“车”况,为什么我们要升级平台

历经一个半月的时间&#xff0c;不管是叫工业互联网平台还是叫工业大数据平台&#xff0c;从1.0版本升级到2.0版本&#xff0c;升级部分包括&#xff1a;客户端&#xff08;网关&#xff09;、服务端&#xff08;数据接收、数据处理、计算服务&#xff09;、底层数据库结构、WE…

朋友

朋友 题目大意&#xff1a; 有两堆数&#xff0c;只有第一堆数会和第二堆数中比自己小的数交“朋友”&#xff0c;问有多少对朋友 原题&#xff1a; 题目描述 mxy 即将前往新世界。 在前往新世界的过程中&#xff0c;ta 遇见了两种人。一种是只和 lowb 做朋友&#xff0c;…

好代码是管出来的——.Net Core中的单元测试与代码覆盖率

测试对于软件来说&#xff0c;是保证其质量的一个重要过程&#xff0c;而测试又分为很多种&#xff0c;单元测试、集成测试、系统测试、压力测试等等&#xff0c;不同的测试的测试粒度和测试目标也不同&#xff0c;如单元测试关注每一行代码&#xff0c;集成测试关注的是多个模…

P3694-邦邦的大合唱站队【状压dp】

正题 题目链接:https://www.luogu.com.cn/problem/P3694 题目大意 nnn个人&#xff0c;有mmm个队伍&#xff0c;每个人都属于一个队伍。要求叫出一些人来&#xff0c;然后任意插入出来的空隙中使得同一队的人在一起。求最少出列人数。 解题思路 如果知道最终的队列就可以十分…

数字图像处理作业

图像处理作业 1 取sT(r)11(mr)EsT(r)\frac{1}{1(\frac{m}{r})^E}sT(r)1(rm​)E1​ 其中rrr为原始亮度&#xff0c;mmm为输入区间的中点&#xff0c;EEE描述曲线的陡峭程度 2 一幅8灰度级图像具有如下所示的直方图&#xff0c;求直方图均衡后的灰度级和对应概率&#xff0c;…

【差分】侦察兵

侦察兵 题目大意&#xff1a; 给出一个图&#xff0c;再给一些点&#xff0c;求出这些点左上角和右下角的数之和 原题&#xff1a; 题目描述 mxy 沉迷于一个辣鸡游戏不可自拔。 游戏地图是一个 n*n 的矩形&#xff0c;在每个单位格子上有一个数字&#xff0c;代表当前位置…

深港澳大湾区(深圳).NET技术交流会圆满成功

2018年7月7日一场以.NET Core微服务和机器学习为主题的交流会成功在深圳职业技术学院落下帷幕。这次活动在短短的一周时间内&#xff0c;报名人数超过了170人&#xff0c;除了一些同学临时有事&#xff0c;基本都到现场了&#xff0c;特别感谢深职院的软创工作室对这次活动的支…

P5322-[BJOI2019]排兵布阵【背包】

正题 题目链接:https://www.luogu.com.cnp/problem/P1768 题目大意 nnn个城池&#xff0c;有sss个玩家对于每个城池有一定的兵。对于所有玩家的每个城池都有派兵一个人数&#xff0c;要求派兵人数之和为mmm。如果你的派兵数列每大于一个玩家派兵数量的两倍那么就可以获得该城池…

图像处理作业二

作业信息 1、证明3.44和3.45的正确性 g(x,y)1K∑i1Kgi(x,y)g(x,y)\frac{1}{K}\sum_{i1}^K g_i(x,y)g(x,y)K1​∑i1K​gi​(x,y) E[g(x,y)]f(x,y)E[n(x,y)]f(x,y)E[g(x,y)]f(x,y)E[n(x,y)]f(x,y)E[g(x,y)]f(x,y)E[n(x,y)]f(x,y) D[g]E[(g−E[g])2]E[g2]−E[g]2E[f22f(n1n2...nK…

【模拟】遭遇战

遭遇战 题目大意&#xff1a; 在一个nn的范围内两个人在跑&#xff08;san&#xff09;步&#xff0c;问他们在哪里相遇 原题 题目描述 mxy 沉迷于一个辣鸡游戏不可自拔。 游戏地图是一个 n*n 的矩形&#xff0c;左上角为&#xff08;0,0&#xff09;&#xff0c;右下角为…

.Net Core开发日志——Peachpie

.Net Core的生态圈随着开源社区的力量不断注入至其中&#xff0c;正在变得越来越强盛&#xff0c;并且不时得就出现些有意思的项目&#xff0c;比如Peachpie&#xff0c;它使得PHP的代码迁移到.Net Core项目变得可能。从创建简单的入门程序开始可以更容易地体会其特性。首先安装…

P5268-[SNOI2017]一个简单的询问【莫队】

正题 题目链接:https://www.luogu.com.cn/problem/P5268 题目大意 nnn个数的一个序列&#xff0c;定义get(l1,r1,x)get(l_1,r_1,x)get(l1​,r1​,x)表示区间[l1,r1][l_1,r_1][l1​,r1​]中有多少个xxx。 每次询问(l1,r1,l2,r2)(l_1,r_1,l_2,r_2)(l1​,r1​,l2​,r2​)求∑x∞…