首页 > 代码库 > 一个简单的动态规划问题
一个简单的动态规划问题
题目来源于POJ,是一道非常基础的动态规划题目。但是却耗费了我非常多的时间,时间复杂度也从N的三次方,降到N的平方,最后优化到0(n)才最终得以通过。
题目如下:
要求其实非常简单,已知给你a1,a2....an,总共n个数,要求你从中抽取出两个连续的子序列,当然,如题意所示,两个序列连续在一起也是OK的,然后将其中最大的序列和输出即可。
看到题目,第一想法非常简单,从n个数中选择一个数k,将A分为1~k和k+1~n两部分。然后求出两部分内各自的最大子序和,合并起来的到Dk,然后从n中情况中选出最大的Dk就是所要求的结果d(A)了。那么每个1~k或是k+1~n中的最大子序列怎么求呢?因为之前刚看过LCS的解法嘛,很简单,构造一个Ci,j。表示ai~aj的序列和。当i=j时,Ci,j就是ai。然后逐步求出j-i的值从1到n-1的所有情况。例如,当j-i更新时,新的Ci,j=ai,i+Ci+1,j。然后再用两个数组bMAX[k],表示以k作为分割,前面k个元素中的最大子序和。同理aMAX[k]为后n-k个元素的最大子序和。并且没有必要保留每个Cij,当j-i加一的时候,将原有的Cij更新,并且更新相应的aMAX[i]和bMAX[i]就可以了。下面是源代码展示:
for(int i=1;i<=n;i++){ scanf("%d",&c[0][i]); c[1][i]=c[0][i]; } bMax[2]=c[0][1]; for(int i=3;i<=n-1;i++) bMax[i]=bMax[i-1]>c[0][i-1]?bMax[i-1]:c[0][i-1]; aMax[n-1]=c[0][n]; for(int i=n-2;i>=2;i--) aMax[i]=aMax[i+1]>c[0][i+1]?aMax[i+1]:c[0][i+1]; for(int gap=1;gap<n;gap++){ for(int i=1;i<=n-gap;i++){ c[1][i]=c[0][i]+c[1][i+1]; bMax[i+gap+1]=bMax[i+gap+1]>bMax[i+gap]?bMax[i+gap+1]:bMax[i+gap]; bMax[i+gap+1]=bMax[i+gap+1]>c[1][i]?bMax[i+gap+1]:c[1][i]; } } for(int gap=1;gap<n;gap++){ for(int i=n;i>=1+gap;i--){ c[1][i]=c[0][i]+c[1][i-1]; aMax[i-gap-1]=aMax[i-gap-1]>aMax[i-gap]?aMax[i-gap-1]:aMax[i-gap]; aMax[i-gap-1]=aMax[i-gap-1]>c[1][i]?aMax[i-gap-1]:c[1][i]; } } int ans=aMax[2]+bMax[2]; for(int i=3;i<n;i++) ans=ans>aMax[i]+bMax[i]?ans:aMax[i]+bMax[i];
不过,上述做法的效率还是太低了。最好的做法时间复杂度完全能在0(n)解决。其实我们可以用动态规划的思维仔细想一下。如果用L[i]表示ai(不包括ai)之前的最大子序列的和。那么L[i]的值只能来源于两种情况。第一L[i]=L[i-1]即前i-1个数的最大子序列不包含新加入的ai-1。还有一种情况就是L[i]=ai-1+prefix。prefix指的是ai-1之前ai-2,ai-3...能达到的最大子序和。当然,在最初的时候prefix是为0的,然后例如在取得L[i]的值后,将prefix更新为prefix+ai-1。当prefix为正的时候,毫无疑问要加上它。但如果它为负数呢?那我们就把它更新为0,也就是不加前缀了。由此通过一次遍历,便能求出所有的L[i]。然后,同理经过一次相同的反向遍历求得R[i](此时包括ai)获得ai之后所有元素的最大子序和。最后再从L[i]+R[i]中取出最小值即可。
具体的代码如下:
L[1]=-10000-1; prefix=0; for(int i=1;i<=n;i++){ scanf("%d",&A[i]); L[i+1]=L[i]>prefix+A[i]?L[i]:prefix+A[i]; if(prefix+A[i]>=0) prefix=prefix+A[i]; else prefix=0; } R[n]=A[n]; prefix=0; for(int i=n-1;i>=1;i--){ R[i]=R[i+1]>prefix+A[i]?R[i+1]:prefix+A[i]; if(prefix+A[i]>=0) prefix=prefix+A[i]; else prefix=0; } int ans=L[2]+R[2]; for(int i=3;i<=n;i++) ans=ans>L[i]+R[i]?ans:L[i]+R[i];
一个简单的动态规划问题