zoj 3211
有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; }