题目链接:
【小结】:
这个题,和我想的不太一样,我真的受到昨天的一个题影响了,一直没有想法,后来大家都说是dp,我才反应回来,不难想到就是dp,但是怎么写这个状态方程呢???这就让我很迷糊了。。。做题做得太少了,所以才写不出来。
【题意】:
这个题目挺有意思的,就是农夫John,然后他有n头牛,然后每一头牛都有各自的打包装的本领,然后农夫John分组(最多为k头牛一组)后可以让组内的所有牛 都获得组内牛中最高能力值,给你n,k,给出n个数,然后可以宁最多为k个连续的数为 该区域内的最大值,然后问,进行这个操作后怎么使得总和最大。
【题解】:
不难想到:dp[ N ],数组内,dp[i],表示以第i个为前缀的最大值,一直维护。是前缀而不是其他,因为如果是单个的最优不能保证全局的最优。 关键是: 然后怎么写出状态方程呢????
首先我们拿到的信息只有k,和k中区间内最大的。
苦死冥想之后,还是做不出来,最后赛后去看洛谷才发现简单的dp。
第一层for枚举1~n,第二层for枚举(写法有两种):
第一种可以是枚举后面的数 Max ( a[ j ] ) ,,使得在该位上变成 (j-i+1) *Max( a[ j ] )+dp[ i-1 ],一直维护最大值。
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+100;
int dp[N];
int main()
{
int n,k,a[N];
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
int maxz=0;
for(int j=i;j<min(i+k,n+1);j++){
maxz = max(maxz,a[j]);
dp[j]= max(dp[j],dp[i-1]+(j-i+1)*maxz);
}
}
printf("%d\n",dp[n]);
return 0;
}
第二种可以枚举长度,但是这个方向是从枚举前面的数字,维护dp[i]的最值。
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+100;
int dp[N];
int main()
{
int n,k,a[N];
scanf("%d%d",&n,&k);
for( int i=1;i<=n;i++ )scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
dp[i]=dp[i-1]+a[i];
int Maxz=a[i];
for(int j=1;j<k;j++){ //注意j<k,而没有等于
if(i-j>0){
Maxz=max(Maxz,a[i-j]);
dp[i]=max(dp[i],dp[i-j-1]+(j+1)*Maxz);
}
}
}
printf("%d\n",dp[n]);
return 0;
}