算法复习——区间dp
Posted AseanA
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法复习——区间dp相关的知识,希望对你有一定的参考价值。
感觉对区间dp也不好说些什么直接照搬讲义了2333
例题:
1.引水入城(洛谷1514)
这道题先开始看不出来到底和区间dp有什么卵关系····
首先肯定是bfs暴力判一判可以覆盖到哪些城市····无解直接输出···有解得话就要想想了····
这道题关键是要发现··如果一个蓄水池所在城市可以覆盖到一些沙漠城市···那么这些沙漠城市肯定是一段连续区间····不然假设有一个城市是断开的而两边都被同一个蓄水池流出的水覆盖,这个城市四周的城市都肯定比它矮···(不理解举个反例吧···反正我举不出来)···然后就可以找到每一个蓄水池它对应的可以覆盖的区间···转化成一个关于线段覆盖的区间dp问题·····即可求解(dp方程看代码吧)
另外注意这道题bfs时注意是入队时打标记不然会爆···还有判条件时先判越界没有···
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int inf=0x3f3f3f3f; const int N=1005; const int gox[4]={1,0,-1,0}; const int goy[4]={0,1,0,-1}; struct node { int x,y; }que[N*N]; struct node1 { int l,r; }g[N]; int map[N][N],n,m,tail,head,f[N]; bool visit[N][N]; inline int R() { char c;int f=0; for(c=getchar();c<\'0\'||c>\'9\';c=getchar()); for(;c<=\'9\'&&c>=\'0\';c=getchar()) f=(f<<3)+(f<<1)+c-\'0\'; return f; } inline void bfs() { head=1,tail=0; for(int i=1;i<=m;i++) { que[++tail].x=1;que[tail].y=i; visit[que[head].x][que[head].y]=true; } for(;head<=tail;head++) { for(int i=0;i<=3;i++) { visit[que[head].x][que[head].y]=true; int vx=que[head].x+gox[i];int vy=que[head].y+goy[i]; if(vx<1||vx>n||vy<1||vy>m||visit[vx][vy]||map[vx][vy]>=map[que[head].x][que[head].y]) continue; node temp; temp.x=vx,temp.y=vy;que[++tail]=temp; visit[temp.x][temp.y]=true; } } } inline void dfs(int x,int y,int u) { visit[x][y]=true; if(x==n) g[u].l=min(g[u].l,y),g[u].r=max(g[u].r,y); for(int i=0;i<=3;i++) { int vx=x+gox[i];int vy=y+goy[i]; if(visit[vx][vy]||vx<1||vx>n||vy<1||vy>m||map[vx][vy]>=map[x][y]) continue; dfs(vx,vy,u); } } int main() { //("a.in","r",stdin); memset(f,inf,sizeof(f)); n=R(),m=R(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) map[i][j]=R(); bfs(); int temp=0; for(int i=1;i<=m;i++) if(visit[n][i]==true) temp++; if(temp!=m) { cout<<"0"<<endl; cout<<m-temp<<endl; return 0; } for(int i=1;i<=m;i++) { memset(visit,false,sizeof(visit)); g[i].l=m+1;g[i].r=0; dfs(1,i,i); } f[0]=0; for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) if(g[j].l<=i&&g[j].r>=i) f[i]=min(f[i],f[g[j].l-1]+1); cout<<"1"<<endl; cout<<f[m]<<endl; return 0; }
2.压缩(bzoj1068)
Description
给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息。压缩后的字符串除了小
写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没
有M,则从串的开始算起)开始的解压结果(称为缓冲串)。 bcdcdcdcd可以压缩为bMcdRR,下面是解压缩的过程
另一个例子是abcabcdabcabcdxyxyz可以被压缩为abcRdRMxyRz。
Input
输入仅一行,包含待压缩字符串,仅包含小写字母,长度为n。
Output
输出仅一行,即压缩后字符串的最短长度。
Sample Input
Sample Output
HINT
在第一个例子中,解为aaaRa,在第二个例子中,解为bMcdRRxMcdRR。
【限制】
100%的数据满足:1<=n<=50 100%的数据满足:1<=n<=50
从这道题可以看出:区间dp不只可以用于解决序列的划分、覆盖问题,还可以用于解决序列的压缩、消除问题·····
解决这一类的通法是枚举左右区间然后再在中间枚举断点
接下来引用DaD3zZ的题解,%%%%%
代码如下:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=105; int f[N][N][2],n; char s[N]; inline bool check(int l,int r) { if((r-l+1)%2==1) return false; int mid=(l+r)/2; for (int i=l; i<=mid; i++) if (s[i]!=s[mid+i-l+1]) return false; return true; } int main() { //freopen("a.in","r",stdin); scanf("%s",s+1); n=strlen(s+1); for(int i=n;i>=1;i--) for(int j=i;j<=n;j++) { f[i][j][0]=f[i][j][1]=j-i+1; for(int k=i;k<j;k++) { f[i][j][1]=min(f[i][j][1],min(f[i][k][0],f[i][k][1])+min(f[k+1][j][0],f[k+1][j][1])+1); f[i][j][0]=min(f[i][j][0],f[i][k][0]+j-k); } if(check(i,j)) f[i][j][0]=f[i][(j+i)/2][0]+1; if(j-i+1==1) f[i][j][1]=n+1; } printf("%d\\n",min(f[1][n][1],f[1][n][0])); return 0; }
3.字符串折叠(bzoj1090 SCOI2003)
Description
折叠的定义如下: 1. 一个字符串可以看成它自身的折叠。记作S S 2. X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S) SSSS…S(X个S)。 3. 如果A A’, BB’,则AB A’B’ 例如,因为3(A) = AAA, 2(B) = BB,所以3(A)C2(B) AAACBB,而2(3(A)C)2(B)AAACAAACBB 给一个字符串,求它的最短折叠。例如AAAAAAAAAABABABCCD的最短折叠为:9(A)3(AB)CCD。
Input
仅一行,即字符串S,长度保证不超过100。
Output
仅一行,即最短的折叠长度。
Sample Input
Sample Output
HINT
一个最短的折叠为:2(NEERC3(YES))
这道题和例题2几乎是一样的,同样的区间压缩dp同样的枚举左右区间,同样的枚举断点,同样的判断压缩合法性,并且状态转移还要简单一点·······就不多说了
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=105; int n,f[N][N],m; char s[N]; inline bool check(int a,int b,int c) { if((c-a+1)%(b-a+1)) return false; int num=(c-a+1)/(b-a+1);m=0; for(int i=a;i<=b;i++) for(int j=i+(b-a+1);j<=c;j+=(b-a+1)) if(s[j]!=s[i]) return false; while(num) m++,num/=10; return true; } int main() { //freopen("a.in","r",stdin); scanf("%s",s+1);n=strlen(s+1); for(int i=n;i>=1;i--) for(int j=i;j<=n;j++) { f[i][j]=j-i+1; if(i==j) continue; for(int k=i;k<j;k++) { if(check(i,k,j)) f[i][j]=min(f[i][j],f[i][k]+m+2); f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]); } } cout<<f[1][n]<<endl; return 0; }
4.Multiplication Puzzle(zoj1602 poj1651)
The multiplication puzzle is played with a row of cards, each containing a single positive integer. During the move player takes one card out of the row and scores the number of points equal to the product of the number on the card taken and the numbers on the cards on the left and on the right of it. It is not allowed to take out the first and the last card in the row. After the final move, only two cards are left in the row.
The goal is to take cards in such order as to minimize the total number of scored points.
For example, if cards in the row contain numbers 10 1 50 20 5, player might take a card with 1, then 20 and 50, scoring
10*1*50 + 50*20*5 + 10*50*5 = 500+5000+2500 = 8000
If he would take the cards in the opposite order, i.e. 50, then 20, then 1, the score would be
1*50*20 + 1*20*5 + 10*1*5 = 1000+100+50 = 1150.
Input
The first line of the input file contains the number of cards N (3 <= N <= 100). The second line contains N integers in the range from 1 to 100, separated by spaces.
Process to the end of file.
Output
Output file must contain a single integer - the minimal score.
Sample Input
6
10 1 50 50 20 5
Sample Output
3650
md这道智障题充分证明我在区间dp上还有待修炼····注意要交这道题去zoj交,poj数据太tm水了···
这道题其实很类似最开始讲义里提到的消除回文子串问题··甚至简单得多··也是一道区间消除问题···
考虑到1,n是不能被消除的,我用f[i][j]表示消除i到j之间的数字最少需要多少分数(注意是之间),枚举中间点k,这里k可以看成最后一个消除的点,推出
f[i][j]=max{f[i][j],f[i][k]+f[k][j]+num[k]*num[i]*num[j]}
注意初始化时f[i][i]=f[i][i+1]=0,其他等于inf········#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=105; int n,num[N],f[N][N]; const int inf=0x3f3f3f3f; inline int R() { char c;int f=0; for(c=getchar();c<\'0\'||c>\'9\';c=getchar()); for(;c<=\'9\'&&c>=\'0\';c=getchar()) f=(f<<3)+(f<<1)+c-\'0\'; return f; } int main() { //freopen("a.in","r",stdin); while(scanf("%d",&n)!=EOF) { memset(f,inf,sizeof(f)); for(int i=1;i<=n;i++) num[i]=R(),f[i][i+1]=f[i][i]=0; for(int i=n-2;i>=1;i--) for(int j=i+2;j<=n;j++) for(int k=i+1;k<j;k++) f[i][j]=min(f[i][j],f[i][k]+f[k][j]+num[i]*num[k]*num[j]); cout<<f[1][n]<<endl; } return 0; }
5.Dire Wolf(hdu5115)
Problem Description
Dire wolves look like normal wolves, but these creatures are of nearly twice the size. These powerful beasts, 8 - 9 feet long and weighing 600 - 800 pounds, are the most well-known orc mounts. As tall as a man, these great wolves have long tusked jaws that look like they could snap an iron bar. They have burning red eyes. Dire wolves are mottled gray or black in color. Dire wolves thrive in the northern regions of Kalimdor and in Mulgore.
Dire wolves are efficient pack hunters that kill anything they catch. They prefer to attack in packs, surrounding and flanking a foe when they can.
— Wowpedia, Your wiki guide to the World of Warcra
Matt, an adventurer from the Eastern Kingdoms, meets a pack of dire wolves. There are N wolves standing in a row (numbered with 1 to N from left to right). Matt has to defeat all of them to survive.
Once Matt defeats a dire wolf, he will take some damage which is equal to the wolf’s current attack. As gregarious beasts, each dire wolf i can increase its adjacent wolves’ attack by bi. Thus, each dire wolf i’s current attack consists of two parts, its basic attack ai and the extra attack provided by the current adjacent wolves. The increase of attack is temporary. Once a wolf is defeated, its adjacent wolves will no longer get extra attack from it. However, these two wolves (if exist) will become adjacent to each other now.
For example, suppose there are 3 dire wolves standing in a row, whose basic attacks ai are (3, 5, 7), respectively. The extra attacks bi they can provide are (8, 2, 0). Thus, the current attacks of them are (5, 13, 9). If Matt defeats the second wolf first, he will get 13 points of damage and the alive wolves’ current attacks become (3, 15).
As an alert and resourceful adventurer, Matt can decide the order of the dire wolves he defeats. Therefore, he wants to know the least damage he has to take to defeat all the wolves.
Input
The second line contains N integers ai (0 ≤ ai ≤ 100000), denoting the basic attack of each dire wolf.
The third line contains N integers bi (0 ≤ bi ≤ 50000), denoting the extra attack each dire wolf can provide.
Output
Sample Input
Sample Output
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=205; const int inf=0x3f3f3f3f; int a[N],b[N],n,T,f[N][N]; inline int R() { char c;int f=0; for(c=getchar();c<\'0\'||c>\'9\';c=getchar()); for(;c<=\'9\'&&c>=\'0\';c=getchar()) f=(f<<3)+(f<<1)+c-\'0\'; return f; } inline int trans(int a) { if(a<0) return 0; else return a; } int main() { //freopen("a.in","r",stdin); T=R(); for(int t=1;t<=T;t++) { memset(f,inf,sizeof(f)); memset(b,0,sizeof(b)); printf("Case #%d: ",t); n=R(); for(int i=1;i<=n;i++) a[i]=R(); for(int i=1;i<=n;i++) b[i]=R(); for(int i=0;i<=n+1;i++) f[i][i]=f[i][i+1]=0; for(int i=n-1;i>=0;i--) for(int j=i+2;j<=n+1;j++) for(int k=i+1;k<j;k++) f[i][j]=min(f[i][j],f[i][k]+f[k][j]+b[i]+b[j]+a[k]); printf("%d\\n",f[0][n+1]); } return 0; }
6.Two Rabbits(hdu4745)
Problem Description
The rabbits jumped from one stone to another. Tom always jumped clockwise, and Jerry always jumped anticlockwise.
At the beginning, the rabbits both choose a stone and stand on it. Then at each turn, Tom should choose a stone which have not been stepped by itself and then jumped to it, and Jerry should do the same thing as Tom, but the jumping direction is anti-clockwise.
For some unknown reason, at any time , the weight of the two stones on which the two rabbits stood should be equal. Besides, any rabbit couldn\'t jump over a stone which have been stepped by itself. In other words, if the Tom had stood on the second stone, it cannot jump from the first stone to the third stone or from the n-the stone to the 4-th stone.
Please note that during the whole process, it was OK for the two rabbits to stand on a same stone at the same time.
Now they want to find out the maximum turns they can play if they follow the optimal strategy.
Input
For each test cases, the first line contains a integer n denoting the number of stones.
The next line contains n integers separated by space, and the i-th integer ai denotes the weight of the i-th stone.(1 <= n <= 1000, 1 <= ai <= 1000)
The input ends with n = 0.
Output
Sample Input
Sample Output
求环的最长不连续回文子串的模板题····详见http://blog.csdn.net/hcbbt/article/details/18949581
代码:
#include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<string> #include<cstring> #include<algorithm> #include<iostream> using namespace std; const int N=1005; int num[N],f[N][N],n; inline int R() { char c;int f=0; for(c=getchar();c<\'0\'||c>\'9\';c=getchar()); for(;c<=\'9\'&&c>=\'0\';c=getchar()) f=(f<<3)+(f<<1)+c-\'0\'; return f; } int main() { //freopen("a.in","r",stdin); while(true) { n=R();if(!n) break; memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) num[i]=R(),f[i][i]=1; for(int l=1;l<n;l++) for(int i=1;i+l<=n;i++) { int j=l+i; f[i][j]=max(max(f[i+1][j],f[i][j-1]),f[i+1][j-1]+(num[i]==num[j]?2:0)); } int ans=0; for(int i=1;i<=n;i++) ans=max(ans,f[1][i]+f[i+1][n]); cout<<ans<<endl; } return 0; }
以上是关于算法复习——区间dp的主要内容,如果未能解决你的问题,请参考以下文章