有n棵樹,m天,每天只能砍一棵樹,每棵樹的初始價值爲ai,每過一天價值增長bi,求m天砍樹得到的最大價值。
開始貪心不可以,n棵樹長m天好像可以分成n*m棵樹再分組揹包(一棵樹只能砍一次)(裝酒問題),dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+w[i]),但是一天有隻能砍一棵樹,有兩個限制條件,直接揹包有後效性,(放第i棵樹可能改變dp[i-1][j]的值,即前面的狀態不能保證是最終的狀態) 不行。
n棵樹,可以砍m天,就是n棵樹中砍了m棵,那麼應如何安排砍這m棵樹的先後呢?顯然m棵全砍掉,a1……am全部得到,考慮價值的增量,要按b的升序依次來砍才能使總和最大,這是高中數學不等式選講裏的一個什麼不等式,大數乘大數相應的和最大。先將樹按照b來升序排列,這樣來保證每一組dp[i][j]中的j棵樹的價值都是按照天數*b遞增的來計算,當前第i棵樹的b最大,也就不可能再放在j天前了,這樣每次的dp[i-1][j]就是最優的結果,既然每個j棵樹的順序能確定,接下來就是在n棵樹裏選m棵,再對樹進行分組揹包就沒有問題了。
方程dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+w[i]),i j升序,dp[i][j]表示在前i棵樹中j天的最大價值也就是i棵樹中砍j棵
和hdu 3466類似,驕傲的商人那個,都是有帶順序的揹包,要考慮揹包問題的本質
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int dp[300][300];
struct node{
int a,b;
}s[300];
int cmp(const void*aa,const void*bb)
{
node*a=(node*)aa;
node*b=(node*)bb;
return a->b > b->b;
}
int max(int a,int b)
{
return a>b?a:b;
}
int n,m;
int main()
{
int i,j,k,l;
scanf("%d",&l);
while(l--)
{
scanf("%d %d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&s[i].a);
for(i=1;i<=n;i++)
scanf("%d",&s[i].b);
qsort(s+1,n,sizeof(s[0]),cmp);
memset(dp,0,sizeof(dp));
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+s[i].a+s[i].b*(j-1));
}
}
printf("%d\n",dp[n][m]);
}
return 0;
}