hdu3033 题目要求与普通的组合背包(每组至多选一个)有所区别,而本题目的话,每组至少选一个,
那么如何保证每组至少选一个呢,问题就在初始化的问题还有状态转移的问题了
if(f[i][l-br[i].b[j]]!=-1 && f[i][l]<f[i][l-br[i].b[j]]+br[i].c[j]) //从当前组再多选一个
f[i][l]=f[i][l-br[i].b[j]]+br[i].c[j];
if(f[i-1][l-br[i].b[j]]!=-1 && f[i][l]<f[i-1][l-br[i].b[j]]+br[i].c[j]) //在上一组的状态下,选当前组的一个
f[i][l]=f[i-1][l-br[i].b[j]]+br[i].c[j];
就上面这段关键的代码
在某一状态存在的情况下(不等于-1),找出三种状态中最大的,赋值给f[i][j] .由于开始时所有f[i][j](i!=0)都为-1,所以更新时一定会从f[0][]开始,而此时就可保证当i=1时,f[i][j]中所有值不为-1的状态一定装了品牌1的某个物品。而当i>1时,
要想得到 最初 的f[i][j],一定是从已经放有前i-1个品牌的某个状态得到的,而更新最初 的f[i][j] 也一定会用到i品牌的某
个产品(都可由状态转移方程可知)。
总之,保证每一类品牌 至少放一件产品是通过赋初值,条件判断,状态转移三方面实现的
#include<iostream> #include<algorithm> #include<string.h> using namespace std; int n,m,k; __int64 f[11][10010]; int num[11]; struct brand { int b[101],c[101]; }br[11]; int main() { while(scanf("%d %d %d",&n,&m,&k)==3) { int a; memset(num,0,sizeof(num)); for(int i=0;i<n;i++) { scanf("%d",&a); a; scanf("%d %d",&br[a].b[num[a]],&br[a].c[num[a]]); num[a]++; } memset(f,-1,sizeof(f)); for(int i=0;i<=m;i++) f[0][i]=0; for(int i=1;i<=k;i++) { for(int j=0;j<num[i];j++) { for(int l=m;l>=0;l--) { if(l<br[i].b[j]) continue; if(f[i][l-br[i].b[j]]!=-1 && f[i][l]<f[i][l-br[i].b[j]]+br[i].c[j]) f[i][l]=f[i][l-br[i].b[j]]+br[i].c[j]; if(f[i-1][l-br[i].b[j]]!=-1 && f[i][l]<f[i-1][l-br[i].b[j]]+br[i].c[j]) f[i][l]=f[i-1][l-br[i].b[j]]+br[i].c[j]; } } } if(f[k][m]<0)puts("Impossible"); else printf("%I64d\n",f[k][m]); } return 0; }
hdu3535 理解了上面3033之后,这道题目其实也差不多,问题就出在赋初值的问题上了,对了每组至少选一个的情况我们已经知道了,但对于,任意选还有至多选一个的情况,初值当然是复制前一组的状态了,可是,如果不复制前一组的状态,后面的状态转移应该怎么处理,我怎么想也想不透
#include<iostream> #include<algorithm> #define maxn 101 #define inf 99999999 using namespace std; struct object { int c,g; }; struct sett { int k,s; object w[110]; }set[110]; int T,n,m,dp[110][maxn]; int main() { while(scanf("%d %d",&n,&T)==2) { for(int i=1;i<=n;i++) for(int j=0;j<=T;j++) dp[i][j]=-inf; for(int i=1;i<=n;i++) { scanf("%d %d",&set[i].k,&set[i].s); for(int j=0;j<set[i].k;j++) scanf("%d %d",&set[i].w[j].c,&set[i].w[j].g); } for(int i=0;i<=T;i++) dp[0][i]=0; for(int i=1;i<=n;i++) { switch(set[i].s) { case 0: for(int j=0;j<set[i].k;j++) for(int t=T;t>=set[i].w[j].c;t--) { int v=set[i].w[j].c,cost=set[i].w[j].g; int last=dp[i-1][t-v]+cost; int cur=dp[i][t-v]+cost; dp[i][t]=max(last,max(dp[i][t],cur)); } break; case 1: for(int j=0;j<=T;j++) dp[i][j]=dp[i-1][j]; for(int j=0;j<set[i].k;j++) for(int t=T;t>=set[i].w[j].c;t--) { int v=set[i].w[j].c,cost=set[i].w[j].g; int last=dp[i-1][t-v]+cost; // dp[i][t]=max(dp[i][t],max(dp[i-1][t],last)); dp[i][t]=max(dp[i][t],last);//要么不选,要么从上一组的状态,在当前组选一个 } break; case 2: for(int j=0;j<=T;j++) dp[i][j]=dp[i-1][j]; for(int j=0;j<set[i].k;j++) for(int t=T;t>=set[i].w[j].c;t--) { int v=set[i].w[j].c,cost=set[i].w[j].g; int cur=dp[i][t-v]+cost; int last=dp[i-1][t-v]+cost; // dp[i][t]=max(dp[i-1][t],max(dp[i][t],max(cur,last))); dp[i][t]=max(dp[i][t],max(last,cur));//比上一种情况多了一种选择, 就是可以从当前组继续多选择一个 } break; } } if(dp[n][T]<0) puts("-1"); else printf("%d\n",dp[n][T]); } return 0; }