题意:给你一串长度为n的数列,叫你求其中k段子序列的和的最大值。
本人愚笨… 画了一个晚上才搞懂怎么回事……好羞愧…
有几点领悟:博士原先讲说,DP就像填表,你先填行还是先填列都可以。现在有了更深入的理解了。
首先DP思想,对于数列中的一个数,对它进行处理,先设dp[i][j]表示前i个数(包括当前的数)j个子序列的和。有两种情况:
1.当前数的为第i个数,在前i-1个数中,若已取满j段,那么现在要做的就是把当前数纳入其中。
2.若没有取满j段,则一定取了j-1段,也就适合说当前这个数自成一段,(因为若是取的段数小于j-1段的话,就会出现要去前面的数中取段数的事情了,但是我们处理的是当前的这个数)。所以说可以列出状态转移方程:dp[i][j]=max(dp[i-1][j],dp[k][j-1])+a[i].其中,k的范围是[j-1,i-1]。因为要在前i-1个数中找到最大的和,但是序列范围肯定不能比段数来的少。嗯,所以这里dp[i-1][j]表示当前这个数只能并入前一个数的那一段区间。
然后这里有个表格,方便理解。是从 http://blog.csdn.net/skillart/article/details/8677423 这里撸的,十分感谢!从这里看懂的!
比如这么一行数据 3 6 2 3 -7 6 4 -5 所得到的f[i][j]数据如图所示
0 | 1 | 2 | 3 | |
0 | 0 | 0 | 0 | 0 |
1 | 0 | 2 | 0 | 0 |
2 | 0 | 5 | 5 | 0 |
3 | 0 | -2 | -2 | -2 |
4 | 0 | 6 | 11 | 11 |
5 | 0 | \ | 15 | 15 |
6 | 0 | \ | \ | 10 |
按照表格走,就觉得很好理解了。
到这里可以大致了解原理,然后剩下的就是优化了。
***********************************
没有怎么理解优化,但是这几天有事拖得太久了,先上代码吧。
#include <stdio.h>
#define maxn 1000001
#define inf 0x3fffffff;
int max(int x,int y)
{
if(x>y) return x;
return y;
}
int min(int x,int y)
{
if(x>y) return y;
else return x;
}
int n,m;
int a[maxn],sum;
int f[maxn],h[maxn];// help array
int main()
{
while(scanf("%d%d",&m,&n)!=EOF)
{
int i,j,k;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
h[i]=f[i]=-inf;
}
for(i=1;i<=n;i++)
{
k=min(i,m);
for(j=1;j<=k;j++)
{
f[j]=max(f[j],h[j-1])+a[i];
h[j-1]=max(h[j-1],f[j-1]);
}
h[j-1]=max(h[j-1],f[j-1]);
}
printf("%d\n",h[m]);
}
return 0;
}