【DP优化】——hdu1024——改变拓扑序降维
Max Sum Plus Plus
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 24897 Accepted Submission(s): 8555
Given a consecutive number sequence S1, S2, S3, S4 ... Sx, ... Sn (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ Sx ≤ 32767). We define a function sum(i, j) = Si + ... + Sj (1 ≤ i ≤ j ≤ n).
Now given an integer m (m > 0), your task is to find m pairs of i and j which make sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + ... + sum(im, jm) maximal (ix ≤ iy ≤ jx or ix ≤ jy ≤ jx is not allowed).
But I`m lazy, I don't want to write a special-judge module, so you don't have to output m pairs of i and j, just output the maximal summation of sum(ix, jx)(1 ≤ x ≤ m) instead. ^_^
Process to the end of file.
题思:给定一个数组,求其分成m个不相交子段和最大值的问题。
思路:(转kuangbin)
设Num为给定数组,n为数组中的元素总数,Status[i][j]表示前i个数在选取第i个数的前提下分成j段的最大值,其中1<=j<=i<=n && j<=m,
状态转移方程为:
Status[i][j]=Max(Status[i-1][j]+Num[i],Max(Status[0][j-1]~Status[i-1][j-1])+Num[i])
乍看一下这个方程挺吓人的,因为题中n的限定范围为1~1,000,000而m得限定范围没有给出,m只要稍微大一点就会爆内存。
但仔细分析后
就会发现Status[i][j]的求解只和Status[*][j]与Status[*][j-1]有关(即满足滚动数组条件)
所以本题只需要两个一维数组即可搞定状态转移。
在进行更进一步的分析
还会发现其实Max(Status[0][j-1]~Status[i-1][j-1])根本不需要单独求取。
在求取now_Status(保存本次状态的数组)的过程中即可对pre_Status(保存前一次状态的数组)进行同步更新。
代码如下:
#include<iostream> #include<string.h> #include<stdio.h> using namespace std; const int maxn=1000100; int n,m; int a[maxn];//数列 //注意两个数组定义的细小差别 int dp[maxn];//dp[i]表示以第 i 个元素结尾 分成j个子串的最大和(包含a[i]) int mx[maxn];//mx[i]表示前 i 个元素分成 j 段可以构成的最大和(不一定有a[i]) int main() { while(scanf("%d %d",&m,&n)!=EOF) { int tmp; for(int i=1;i<=n;i++) scanf("%d",&a[i]); memset(dp,0,sizeof(dp)); memset(mx,0,sizeof(mx)); for(int j=1;j<=m;j++)//分成 j 个子串 { //当前状态即可确定子段数一致 tmp=-0x7fffffff;//改变区间个数的时候才赋值 for(int i=j;i<=n;i++)//前 i 个元素 { //要么把a[i]放到前一个集合里,那就是从当前子段个数转移过来,子段个数不变 //要么把a[i]令成一个新的子串,那就是从前一个子段个数转移过来,子段个数+1 dp[i]=max(dp[i-1]+a[i] , mx[i-1]+a[i]); // i 个元素不可能分成 i+1 份 mx[i-1]=tmp; //之后更新tmp值为当前位置j个子串的最优值 tmp=max(tmp,dp[i]); } } printf("%d\n",tmp); } return 0; }
题后感:
先说说0-1滚动,其实这就是两个数组放到一块了,反而不好理解
然后说说自己的一些感受:
动态规划果然非常考验思考能力,
这道题首先是分析,看到数组太大,就需要想到降维
(这里的降维不单是减掉一个方括号,降低一个维度的上限也算是降维)
然后是考虑降维后如何确保满足无后效性性,并找到最优子结构
这道题非常奇妙的一点就在于:通过改变拓扑序,利用 拓扑序的限制 去实现状态的正确转移
把相邻状态放在两个数组里保存,就实现了降维的目的。

浙公网安备 33010602011771号