HDU 1024
一道典型的的动态规划题目,重点在与找到一种正确的状态规划方法,然后建立不同状态间转换的方程式。
题意大致如下:给你n个数,要你在这n个数中取m段数(每段无交集),使这m段之和最大。
每次输入格式:m,n,a1,a2,....an。
一直读到文件尾。
状态方程转换公式:dp[i][j]=max(dp[i][j-1]+arr[j] , max(dp[i-1][k])+arr[j])。(k<j)
可以这样理解,dp[i][j]是以第j个数结尾(包含j)取i段数时能得到的最大和,分两种状态能完全的包含每个情况,一是当第j个数不是单独成段(dp[i][j-1]+arr[j]),二是第j个数单独成段(max(dp[i-1][k])+arr[j])。这样考虑合理的原因就不解释了,看着公式很好想。
然后就是两个细节吧,一是数组存放,要用滚动数组,不然存不下来。(从dp[i][0]到dp[i][n]其实只用存上一个状态数组),二是max(dp[i-1][k]),因为用了滚动数组,所以我们只有dp[x][j-1]的数据。于是我们可以用一个maxnn[x]的数组来记录目前得到的取x段能达到的最大和的值。(1<=x<=m)这样不仅记录了我们所需要的数据,而且每次计算时也节省了时间(因为maxnn[i-1]==max(dp[i-1][k]) )。注意每次计算完一轮dp[k][j]后更新maxnn[x]就好。
代码如下:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e6+10;
const int inf=0x08080808;
int arr[maxn];
int f[maxn];
int maxnn[maxn];
int Max(int a,int b){
return a>b?a:b;
}
int sum(int n){
int ans=0;
for(int i=1;i<=n;++i)
ans+=arr[i];
return ans;
}
int main(){
int m,n;
while(scanf("%d%d",&m,&n)!=EOF){
for(int i=1;i<=n;++i)
scanf("%d",&arr[i]);
for(int i=1;i<=m;++i)
maxnn[i]=-inf;
f[1]=arr[1];
maxnn[1]=arr[1];
maxnn[0]=0;
for(int i=2;i<=n;++i){
for(int j=1;j<=m;++j){
if(j==i){
f[j]=sum(j);
break;
}
f[j]=Max(f[j]+arr[i],maxnn[j-1]+arr[i]);
}
for(int k=1;k<=m;++k){
if(k>i) break;
if(f[k]>maxnn[k])
maxnn[k]=f[k];
}
}
printf("%d\n",maxnn[m]);
}
return 0;
}