程序设计思维与实践 Week12 作业C 区间DP

题意:

程序设计思维与实践 Week12 作业C 区间DP_第1张图片
input:

输入m,输入n。后面跟着输入n个ai 。

output:

输出最大和。

样例:

输入:
1 3 1 2 3
2 6 -1 4 -2 3 -2 3
输出:
6
8

思路:

考虑状态dp[i][j],表示在选取第j个数的时候,前面的数分成i组的最大和。状态转移方程为:
dp[i][]j=max{dp[i][j-1]+a[j],dp[i-1][k]+a[j]},其中dp[i][j-1]表示前面j-1的已经分为i组了,加入a[j]与其相连,没有改变组数。dp[i-1][k],表示在前面的k的数的时候,分为了i-1组,再加入a[j]即又多加入了一组。考虑到前面的i-1组,每组最少一个数,故k的范围i-1到j-1。初始可将dp[i][j]全置为0。但考虑到n为1e6,开个二维数组的空间太大。然后每一轮更新的的时候,只用到了dp[i][j]和dp[i-1][j],即只需要两个数组即可。然后根据前面的状态转移方程,分别将dp[i][]j用dpi2[],dp[i-1]用dpi1[]来表示。即每一轮用dpi1[]记录下上一轮的数据。(即使跟着原来的转移方程变化,也是很艰难。。)需要用一个变量每次记录下更新后的dpi2[j],由状态转移方程可以知道下一轮会用到此时的dpi1[j],即dp[i-1][j-1]。由dp[i-1]知道,这个值还是用的上一轮的。所以如果再得到dpi2[j]的时候,立即更新给dpi1,就会在下一轮被用到,即使用了错误的值。然后就是k的遍历,在这里每次循环中记录下的都是当前的max,故可以省去k的遍历,即直接使用j-1。

总结:

线性规划虽然代码不长,真的巨难。。。很难想到,然后就算知道了状态和转移方程,循环怎么写也是个头疼的地方,然后有的题,数据大,还得把二维的状态压缩。然后因为是压缩了,所以得手动更新记录数据,然后因为有前后两个维度的变化,然后数据在什么时候也得考虑清楚。我太难了。

代码:

#include 
#include 
#include  
#include 
using namespace std;

int m,n;
int a[1000010];
int dpi1[1000010];  //dp[i-1][j]
int dpi2[1000010];  //dp[i][j]
const int inf=-1e8;

int main(){
	while(scanf("%d%d",&m,&n)!=EOF){
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
		}
		memset(dpi2,0,sizeof(dpi2));
		memset(dpi1,0,sizeof(dpi1));
		
		int result;
		for(int i=1; i<=m;i++){  //分成m组 
			result=inf;
			for(int j=i;j<=n;j++){  //从
			    dpi2[i-1]=inf;
				dpi2[j]=max(dpi1[j-1]+a[j],dpi2[j-1]+a[j]);
				
				dpi1[j-1]=result;  //记录下上一轮的 
				result=max(dpi2[j],result);
			}
		}
		cout<<result<<endl;
	}
	return 0;
} 

你可能感兴趣的:(程序设计思维与实践 Week12 作业C 区间DP)