区间DP

区间dp,顾名思义就是在一段区间上进行动态规划。对于每段区间,他们的最优值都是由几段更小区间的最优值得到,是分治思想的一种应用,将一个区间问题不断划分为更小的区间直至一个元素组成的区间,枚举他们的组合 ,求合并后的最优值。
核心思想:
既然让我求解在一个区间上的最优解,那么我把这个区间分割成一个个小区间,求解每个小区间的最优解,再合并小区间得到大区间即可。所以在代码实现上,我可以枚举区间长度len为每次分割成的小区间长度(由短到长不断合并),内层枚举该长度下可以的起点,自然终点也就明了了。然后在这个起点终点之间枚举分割点,求解这段小区间在某个分割点下的最优解。
例题1:
石子归并问题:
n堆石子排成一排,现在要将这n堆石子合并成一堆。规定,每 次只能选取相邻的两堆进行合并。每次合并的花费为这两堆的 石子总数。 • 求出经过n-1次合并后,所需要的最小花费。
状态转移方程:dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j])
例题2:
括号匹配问题:
定义一个合法的括号序列
• 1. 空序列合法
• 2. S合法,那么(S)和[S]合法
• 3. 假设A和B合法,那么AB合法
• 合法序列 - (), [], (()), ([]), ()[], ()[()]
• 非法序列 - (, [, ], )(, (]), ([(),([)]
• 给定一个序列,求最少添加多少个括号,才能得到一个合法序列。 输出总长度减去需要添加的括号的个数。
• Simple Input: ([()[
• Simple Output: 2

#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 99999999
using namespace std;
string s;
int dp[1010][1010];
int main(){
	while(cin>>s&&s!="end"){
		memset(dp,0,sizeof(dp)); 
		int len=s.size() ;
		int i,j;
		for(int l=1;l<len;l++){
			for(i=0,j=l;j<len;i++,j++){
				if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[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]);
			}
		}
		cout<<dp[0][len-1]<<endl;
	}
	return 0;
}

例题3:

东东每个学期都会去寝室接受扫楼的任务,并清点每个寝室的人数。
每个寝室里面有ai个人(1<=i<=n)。从第i到第j个宿舍一共有sum(i,j)=a[i]+…+a[j]个人
这让宿管阿姨非常开心,并且让东东扫楼m次,每一次数第i到第j个宿舍sum(i,j) 问题是要找到sum(i1, j1) + … +
sum(im,jm)的最大值。且ix <= iy <=jx和ix <= jy <=jx的情况是不被允许的。也就是说m段都不能相交。 注:1
≤ i ≤ n ≤ 1e6 , -32768 ≤ ai ≤ 32767 人数可以为负数。。。。(1<=n<=1000000)
输入m,输入n。后面跟着输入n个ai,输出最大和 。
Sample Input
1 3 1 2 3
2 6 -1 4 -2 3 -2 3
Sample Output
6
8

设dp[i][j]为前j个数取i段且必拿j的最大和;则dp[i][j]=max(dp[i][j-1]+a[j],dp[i-1][k]+a[j])(i<=k<j).前者表示拿a[j]并不增加段数,后者表示a[j]是新的一段。然后发现需要空间优化,将二维数组降到一维。观察发现dp[i][j]只和同层或者上一层的有关系,所以我们可以新开一个数组记录上一层的最优解f[],f[j]和dp[j]的区别就是,每层的f[j]都是上一层的dp[j]。
状态转移方程:dp[j]=max(dp[j-1]+a[j],f[j-1]+a[j]).

#include<iostream>
#include<cstdio>
#include<cstring>
#define inf 99999999
using namespace std;
int m,n;
int a[1000010],dp[1000010],f[1000010];
int main(){
	while(cin>>m>>n){
		for(int i=1;i<=n;i++)scanf("%d",&a[i]);
		int ans;
		memset(dp,0,sizeof(dp));
		memset(f,0,sizeof(f));
		for(int i=1;i<=m;i++){
			ans=-inf;
			for(int j=i;j<=n;j++){
				dp[j]=max(dp[j-1]+a[j],f[j-1]+a[j]);
				f[j-1]=ans;
				ans=max(dp[j],ans);
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值