题意:
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;
}