区间dp小结
来源:互联网 发布:长虹社区软件下载 编辑:程序博客网 时间:2024/06/06 05:22
区间dp顾名思义就是在一个区间上进行的一系列动态规划,一般就是通过将区间划分成更小的区间,在小的区间中找到最优解,或者是其他的某些操作
一:基本题型
一般区间dp主要涉及两类问题
1.区间最优解
一般是枚举区间的分界点,将区间分割,然后将子区间的最优解合并为原区间的最优解
2.区间计数
区间计数也要分割区间,而且要做到不重叠,不遗漏
总之,区间dp问题解决的关键就是区间的分割
二.例题分析
1.石子归并
题目描述:
- 输入
- 有多组测试数据,输入到文件结束。
每组测试数据第一行有一个整数n,表示有n堆石子。
接下来的一行有n(0< n <200)个数,分别表示这n堆石子的数目,用空格隔开 - 输出
- 输出总代价的最小值,占单独的一行
- 样例输入
31 2 3713 7 8 16 21 4 18
- 样例输出
9239
状态转移:dp[i][j]=min{dp[i][k]+dp[k+1][j]} k=(i~j-1)
ac代码:
#include <cstdio>#include <cstring>#include <algorithm>#define N 210using namespace std;int dp[N][N],sum[N];int main(){ int n; while(scanf("%d",&n)!=-1) { sum[0]=0; int m; for(int i=1;i<=n;i++) { scanf("%d",&m); sum[i]=sum[i-1]+m; } memset(dp,0,sizeof(dp)); for(int l=2;l<=n;l++) { for(int i=1,j=l;i<=n-l+1;j++,i++) { dp[i][j]=dp[i][i]+dp[i+1][j]+sum[j]-sum[i-1]; for(int k=i+1;k<j;k++) dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]); } } printf("%d\n",dp[1][n]); } return 0;}
2.括号匹配问题:
问题描述(poj 2955):给出一些一些括号,然后计算一下它的最大匹配
思路:括号的最大匹配也是区间dp中非常典型的问题,dp[i][j]表示区间i到j之间的最大匹配
状态转移方程:if(i与j匹配)dp[i][j]=dp[i+1][j-1]+2;
else dp[i][j]=max(dp[i][k]+dp[k+1][j]) k=(i~j-1)
ac代码:
#include <iostream>#include <cstdio>#include <cstring>using namespace std;char a[1005];int dp[1005][1005];int pos[1005][1005];int main(){ while(gets(a)) { if(a[0]=='e') break; int len=strlen(a); memset(dp,0,sizeof(dp)); for(int i=1;i<len;i++) { for(int j=0,k=i;k<len;j++,k++) { if((a[j]=='('&&a[k]==')')||(a[j]=='['&&a[k]==']')) dp[j][k]=dp[j+1][k-1]+2; for(int h=j;h<k;h++) { dp[j][k]=max(dp[j][k],dp[j][h]+dp[h+1][k]); } } } cout<<dp[0][len-1]<<endl; } return 0;}
加强版:
问题描述(poj 1141):不是让我们求出最大匹配,而是让我们求出最少再添加多少个能够使所有括号都匹配,并且将最后的括号情况输出一下
思路:第一步其实非常简单
添加个数=原来总数-匹配数(那剩下的再分别添加一个)
但是我们应该怎么输出呢?
这就需要我们在算最大匹配的过程中标记一些东西,vis[i][j]表示的是我们在计算区间i到j的过程中所取的最优分割点的位置,如果此时的i与j正好匹配的话vis[i][j]=-1,然后递归输出就可以了
ac代码:
#include <iostream>#include <cstdio>#include <cstring>using namespace std;char a[105];int dp[105][105];int vis[105][105];void prin(int x,int y){ if(x>y) return; if(x==y) { if(a[x]=='('||a[x]==')') cout<<"()"; else cout<<"[]"; return; } else { if(vis[x][y]==-1) { cout<<a[x]; prin(x+1,y-1); cout<<a[y]; } else { prin(x,vis[x][y]); prin(vis[x][y]+1,y); } }}int main(){ while(gets(a)) { int len=strlen(a); memset(dp,0,sizeof(dp)); for(int i=1;i<len;i++) { for(int j=0,k=i;k<len;j++,k++) { if((a[j]=='('&&a[k]==')')||(a[j]=='['&&a[k]==']')) {dp[j][k]=dp[j+1][k-1]+2; vis[j][k]=-1; } for(int h=j;h<k;h++) { if(dp[j][h]+dp[h+1][k]>=dp[j][k]) { dp[j][k]=dp[j][h]+dp[h+1][k]; vis[j][k]=h; } } } } prin(0,len-1); cout<<endl; } return 0;}
3.整数划分
问题描述:问题是我们经常见到的整数划分,给出两个整数 n , m ,要求在 n 中加入m - 1 个乘号,将n分成m段,求出这m段的最大乘积
思路:其实和上边的题思路都差不多啦,都属于区间最优解的问题
状态转移方程:dp[i][j]=max(dp[i][j],dp[k][j-1]*data[k+1][i]) k=(j~i)
ac代码:
#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <queue>using namespace std;char a[25];long long data[25][25];long long dp[25][25];int m;int main(){ int t; cin>>t; while(t--) { scanf("%s%d",a,&m); m--;//可以在最后一位后边加上一个乘号,先减去 int len=strlen(a); memset(data,0,sizeof(data)); memset(dp,0,sizeof(dp)); for(int i=0;i<len;i++) { for(int j=i;j<len;j++) { data[i][j]=data[i][j-1]*10+(a[j]-'0'); } } for(int i=0;i<len;i++) dp[i][0]=data[0][i]; for(int i=0;i<len;i++) { for(int j=1;j<=i;j++) { for(int k=j;k<i;k++) dp[i][j]=max(dp[i][j],dp[k][j-1]*data[k+1][i]); } } cout<<dp[len-1][m]<<endl; } return 0;}
好像全是区间最优解的例子哎,等做到区间计数的时候再补充吧@^^@
- 区间dp小结
- 区间DP小结
- 区间dp小结
- 区间dp小结
- 区间dp 小小结
- 区间dp小结
- 区间DP小结(附经典例题)
- 区间DP
- 区间DP
- 区间DP
- 区间DP
- ##区间dp##
- 区间dp
- 区间DP
- 区间dp
- 区间dp
- 区间dp
- 区间dp
- 可直接嵌入业务系统为终端客户提供分析服务的阿里云分析型数据库
- 为编辑框控件或是静态文本控件中的文本添加颜色
- python中移位操作 问题
- 逆向工程
- lvm
- 区间dp小结
- Java反射机制--是什么,为什么,怎么用。
- Java类和对象的理解
- php常用的加速缓存的扩展
- GO语言入门学习网站
- 什么是JSP技术?
- 微信支付证书问题(C#)
- 在Spring Boot中使用@Scheduled实现定时任务
- 鼠标上移、移走、点击事件