n个物品,m元钱,k种种类。
求每个种类至少取一个物品,所能得到的最大价值。
分类里互斥的分组背包,每种类别最多取一个。
for all 组k
for u = V to 0
for i 属于k
dp[ k ][ u ] = max( dp[ k - 1 ][ u ], dp[ k ][ u - c[ i ] ] + v[ i ]);//k - 1这项说明对于k组的物品可以一个都不取,直接转移到第k类。
1.空间可以简化为一维,for ... for ... for ....dp[ u ] = max( dp[ u ], dp[ u - c[ i ] ] + v[ i ]);
2.递推中,先考虑容量u,再考虑物品i,所以在计算时,状态转移,就是每组的物品之间排斥,求max,要么取1个,要么不取。
而至少取一个的分组背包
for all 组k
for i 属于 k
for u = V to 0
if dp[ k - 1][ u - c[ i ] ] + v[ i ] >= 0//dp初始化为-1,表示状态无效,即有一组一个都选不了
dp[ k ][ u ] = max( dp[ k ][ u ],dp[ k - 1][ u - c[ i ] ] + v[ i ] );//由上一组转移来,则第k组必选
if dp[ k ][ u - c[ i ] ] + v[ i ] >= 0
dp[ k ][ u ] = max( dp[ k ][ u ],dp[ k ][ u - c[ i ] ] + v[ i ] );//由同一组转移来,则第k组可以多选
1.不能简化为一维,且最终答案在dp[ k ]里求max
2.递推中,先考虑物品,再考虑容量,在计算时,就是,先尽可能地放物品i,至放不下,就开始放物品i + 1,物品i + 1放入已放了物品i的背包,是相加相容的关系。
#include <iostream>
#include <vector>
#include <map>
#include <cstring>
#include <algorithm>
using namespacestd;
const int maxn =100 + 5;
const int maxk =10 + 5;
const int maxm =10000 + 5;
const int INF =1 << 30;
struct snk{
int a,b,c;
}sk[maxn];
int dp[maxk][maxm];
vector<int> gp[maxk];
int main()
{
int n,m,k;
while(scanf("%d%d%d",&n,&m,&k) !=EOF)
{
for (int i =1; i <= k; i ++) {
gp[i].clear();
}
memset(dp,-1,sizeof(dp));
for (int i =0; i < n; i ++) {
scanf("%d%d%d",&sk[i].a,&sk[i].b,&sk[i].c);
gp[sk[i].a].push_back(i);
}
int min_c =0;
for (int i =1; i <= k; i ++) {
int t =INF;
for (int j =0; j < gp[i].size(); j ++) {
t = min(t,sk[gp[i][j]].b);
}
min_c += t;
}
dp[0][0] =0;
for (int i =1; i <= k ; i ++) {
for (int j =0; j < gp[i].size(); j ++) {
for (int u = m; u >=sk[gp[i][j]].b; u --) {
if(dp[i][u -sk[gp[i][j]].b] >=0)
dp[i][u] =max(dp[i][u],dp[i][u -sk[gp[i][j]].b] +sk[gp[i][j]].c);
if(dp[i -1][u - sk[gp[i][j]].b] >= 0)
dp[i][u] =max(dp[i][u],dp[i -1][u - sk[gp[i][j]].b] + sk[gp[i][j]].c);
}
}
}
int max_v = *max_element(dp[k],dp[k] + m + 1) ;
if (min_c > m) {
printf("Impossible\n");
}
else printf("%d\n",max_v);
}
return0;
}