这段时间学习了区间DP,所以试着把学到的东西稍作总结,以备不时之需。
学习区间DP首先要弄清区间DP是为了解决什么问题:一般的DP主要是特征是一次往往只操作一个数值或者存储可以不连续的物品的状态(比如背包问题中,每一个点存储的状态中,拿到的物品的编号并不要求一定连续)而区间DP,则是要求DP数组中每个点的状态映射到原本的数据集中都会与附近的元素有所关联,其中,连续区间的长度、起始点的位置、子区间分割点的位置都会有所变化,而这和一般的DP有所不同,故一般用区间DP。
区间DP的一般格式(参考博客):
for(int l=1;l<=n;l++) //每次处理的区间的长度 for(int i=0;i+l-1<len;i++) //每次区间的起点 int j=i+l-1; //每次区间的终点 for(int k=i;k<j;k++) //区间的分割点 //TODO:状态转移方程
两个个例题:
一.POJ1561
AC代码:
#include <stdio.h> #include <iostream> #include <string.h> using namespace std; const int maxn=105; int dp[maxn][maxn]; int num[maxn]; int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&num[i]); } for(int l=2;l<n;l++){ for(int i=2;i+l<=n+1;i++){ dp[i][i+l-1]=100000000; for(int k=i;k<i+l-1;k++){ dp[i][i+l-1]=min(dp[i][i+l-1],dp[i][k]+dp[k+1][i+l-1]+num[i-1]*num[k]*num[i+l-1]); } } } printf("%d ",dp[2][n]); return 0; }
二.POJ2955
AC代码:
#include <stdio.h> #include <string.h> #include <iostream> using namespace std; const int maxn=105; char str[maxn]; int dp[maxn][maxn]; int check(char a,char b){ if(a=='('&&b==')'){ return 1; } if(a=='['&&b==']'){ return 1; }else{ return 0; } } int main(){ while(scanf("%s",str)!=EOF){ if(str[0]=='e')break; int len=strlen(str); memset(dp,0,sizeof(dp)); for(int i=0;i<len;i++){ dp[i][i]=0; if(check(str[i],str[i+1]))dp[i][i+1]=2; else dp[i][i+1]=0; } for(int l=3;l<=len;l++){//区间的长度 for(int i=0;i+l-1<len;i++){//开始的位置 int j=i+l-1; if(check(str[i],str[j]))dp[i][j]=dp[i+1][j-1]+2; for(int k=i;k<j;k++){ //区间的分割 dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]); } } } printf("%d ",dp[0][len-1]); } return 0; }