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

题意:

在这里插入图片描述
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 <iostream>
#include <cstdio>
#include <cstring> 
#include <string>
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;
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值