版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37129433/article/details/82860570
AreYouBusy
题意:给你n个物品的集合,每个物品的集合有m个物品,以及该集合的类型s,每个物品都有val和cost属性,如果集合的类型属于0,则该集合的物品至少选一个,如果集合类型属于1,则该集合最多选择一个物品,如果该集合类型属于2,那么该集合的物品可以任选。
现在小明的背包的容量为t,求最大的val值。
数据范围:
输入样例:
3 3
2 1
2 5
3 8
2 0
1 0
2 1
3 2
4 3
2 1
1 1
3 4
2 1
2 5
3 8
2 0
1 1
2 8
3 2
4 4
2 1
1 1
1 1
1 0
2 1
5 3
2 0
1 0
2 1
2 0
2 2
1 1
2 0
3 2
2 1
2 1
1 5
2 8
3 2
3 8
4 9
5 10
输出样例:
5
13
-1
-1
思 路:首先定义
,到第i个集合容量为j时的最大价值。对于当前集合i类型为0时,至少选择一个,可以初始化
,这样就变成一个0,1背包了,状态转移方程为:
两者位置不可以变化,否则可能当cost[i] = 0时,这个包可能会多次选择。对于当前集合i类型为1时,最多选择一个,那么当前集合的状态只能有上一个集合的状态转移过来。
。
如果当前集合的类型是3的话那么就是一个简单的0,1背包了。和状态为0的不同就是可以不选。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1e4+5;
int dp[100+5][maxn];
int val[100+5],cost[100+5];
int n,t;
int m,s;
int main(){
//freopen("input.txt","r",stdin);
while(~scanf("%d %d",&n,&t)){
memset(dp,0,sizeof(dp));
for(int pp=1;pp<=n;pp++){
scanf("%d %d",&m,&s);
for(int i=1;i<=m;i++){
scanf("%d %d",&cost[i],&val[i]);
}
if(s == 0){ //至少选一个
for(int i=0;i<=t;i++)dp[pp][i] = -INF;
for(int i=1;i<=m;i++){
for(int j=t;j>=cost[i];j--){
dp[pp][j] = max(dp[pp][j],dp[pp][j-cost[i]]+val[i]); //这个顺序不能换哈
dp[pp][j] = max(dp[pp][j],dp[pp-1][j-cost[i]]+val[i]);
}
}
}else if(s == 1){ //最多选一个
for(int i=0;i<=t;i++) dp[pp][i] = dp[pp-1][i];
for(int i=1;i<=m;i++){
for(int j=t;j>=cost[i];j--){
dp[pp][j] = max(dp[pp][j],dp[pp-1][j-cost[i]]+val[i]);
}
}
}else if(s == 2){ //随意选不选
for(int i=0;i<=t;i++)dp[pp][i] = dp[pp-1][i];
for(int i=1;i<=m;i++){
for(int j=t;j>=cost[i];j--){
dp[pp][j] = max(dp[pp][j],dp[pp][j-cost[i]]+val[i]);
dp[pp][j] = max(dp[pp][j],dp[pp-1][j-cost[i]]+val[i]);
}
}
}
}
dp[n][t] = max(dp[n][t],-1);
printf("%d\n",dp[n][t]);
}
return 0;
}