题意:
有n个集合,每个集合中有m个元素。求在背包容量为T的情况下,取得元素的总“价值”最大。
这些集合又分为3种类型(用于限制取元素):
0.在这个集合中,至少取一个元素。
1.在这个集合中,最多取一个元素。
2.在这个集合中,可以任意数量的元素。
题解:
混合背包。
对于不同的集合(限制),用不同的方法(主要差别在怎么处理上次得到的最优解)。
对于类型 1 的集合。上次的最优解是不能更改的。
代码:
#pragma GCC optimize(2,"Ofast","inline")
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
#include<cstdlib>
#include<bitset>
#include<functional>
#include<ctime>
#define F(i,s,t) for(int i=(s);i<=(t);i++)
#define D(i,s,t) for(int i=(s);i>=(t);i--)
#define dBug(i) printf("Value=%d\n",i)
#define ddBug(i,j) printf("Value=%d %d\n",i,j)
#define ed putchar('\n')
#define FO freopen("D:\\in.txt","r",stdin)
#define IOS cin.tie(0) ,cout.tie(0), cout.sync_with_stdio(0)
typedef long long ll;
const int INF = 1 << 30;
//const double EPS = 1e-6;
//#define MX 1002
//#define Mod 10000
using namespace std;
struct Job
{
int c, g;
};
Job J[110];
int dp[110][110];
int n, T;//第一行输入的值,n表示number of sets,T表示现有的时间
int m, s;//m表示number of jobs,s表示type of jobs
int main()
{
IOS;
//FO;
while (scanf("%d %d", &n, &T) != EOF){
F(i, 1, n){//有n种工作
scanf("%d %d", &m, &s);
F(j, 1, m)//第i种工作有m个
scanf("%d %d", &J[j].c, &J[j].g);
if (s == 0){//choose at least 1
F(k, 0, T)//取至少一种
dp[i][k] = -INF;
//下面是01背包了
F(j, 1, m)//遍历物品
D(k, T, J[j].c)//背包容量
//把dp[i][k]赋值为无穷小,所以一定会更dp[i-1][k]的哈(因为它的价值和大)。
dp[i][k] = max(dp[i][k], max(dp[i][k - J[j].c], dp[i - 1][k - J[j].c]) + J[j].g);
}//if (s == 0)
else if (s == 1){//choose at most 1
F(k, 0, T)
dp[i][k] = dp[i - 1][k];
F(j, 1, m)
D(k, T, J[j].c)
//每次dp[i][k]是否更新取决于dp[i-1][k],而dp[i-1][k]又是不会改变的
dp[i][k] = max(dp[i][k], dp[i - 1][k - J[j].c] + J[j].g);
}//else if (s == 1){
else{
F(k, 0, T)
dp[i][k] = dp[i - 1][k];
//下面是01背包了
F(j, 1, m)
D(k, T, J[j].c)
dp[i][k] = max(dp[i][k], dp[i][k - J[j].c] + J[j].g);
}//else
}//extren for
if (dp[n][T] > 0)
printf("%d\n", dp[n][T]);
else
printf("-1\n");
}//while
}