# 背包九讲系列2——混合背包、二维费用背包、分组背包

#### 4 混合三种背包问题

##### 4.1 问题

4.2 01 背包与完全背包的混合

4.3 再加上多重背包

#### 5 二维费用的背包问题

##### 5.5 小结

``````Problem Description

Input

Output

Sample Input
3 10 105 3 0 1104 3 4 1202 3 1 130

Sample Output
370
``````

``````#include <iostream>
using namespace std;

const int N=150;
const int P=5000;

int pg[N+1]; //pg支付费用
int rune[N+1]; //rune支付费用
int dp[P+1][P+1];  //dp[i][j]=k 表示花费pg为i rune为j时的最大价值为k
int s[N+1]; // 件数
int v[N+1]; //能力值

int max(int a,int b)
{
return a>b?a:b;
}

void ZeroOnePack(int dp[][P+1],int weight_1,int weight_2,int total_1,int total_2,int value)
{
int j,i;
for(i=total_1;i>=weight_1;i--)
{
for(j=total_2;j>=weight_2;j--)
{
dp[i][j]=max(dp[i][j],dp[i-weight_1][j-weight_2]+value);
}
}
}

void completePack(int dp[][P+1],int weight_1,int weight_2,int total_1,int total_2,int value)
{
int j,i;
for(i=weight_1;i<=total_1;i++)
{
for(j=weight_2;j<=total_2;j++)
{
dp[i][j]=max(dp[i][j],dp[i-weight_1][j-weight_2]+value);
}
}
}

void mutiPack(int dp[][P+1],int weight_1,int weight_2,int total_1,int total_2,int amount,int value)
{
if(amount*weight_1>total_1&&amount*weight_2>total_2)
{
completePack(dp,weight_1,weight_2,total_1,total_2,value);
}
else
{
int k=1;
while(amount-k>=0)
{
ZeroOnePack(dp,k*weight_1,k*weight_2,total_1,total_2,k*value);
amount-=k;
k*=2;
}
ZeroOnePack(dp,amount*weight_1,amount*weight_2,total_1,total_2,amount*value);
}
}

int main()
{
int n,P,R;
cin>>n>>P>>R;

int i;

int p,r,num,val;
for(i=0;i<n;i++)
{
cin>>p>>r>>num>>val;
pg[i]=p;
rune[i]=r;
s[i]=num;
v[i]=val;
}

for(i=0;i<n;i++)
{
if(s[i]==1)//01 背包
{
ZeroOnePack(dp,pg[i],rune[i],P,R,v[i]);
}
else if(s[i]==0)// 完全背包
{
completePack(dp,pg[i],rune[i],P,R,v[i]);
}
else //多重背包
{
mutiPack(dp,pg[i],rune[i],P,R,s[i],v[i]);
}
}

cout<<dp[P][R]<<endl;

return 0;
}

``````

#### 6 分组的背包问题

##### 6.3 小结

``````ACboy needs your help

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 7535    Accepted Submission(s): 4164

Problem Description
ACboy has N courses this term, and he plans to spend at most M days on study.Of course,the profit he will gain from different course depending on the days he spend on it.How to arrange the M days for the N courses to maximize the profit?

Input
The input consists of multiple data sets. A data set starts with a line containing two positive integers N and M, N is the number of courses, M is the days ACboy has.
Next follow a matrix A[i][j], (1<=i<=N<=100,1<=j<=M<=100).A[i][j] indicates if ACboy spend j days on ith course he will get profit of value A[i][j].
N = 0 and M = 0 ends the input.

Output
For each data set, your program should output a line which contains the number of the max profit ACboy will gain.

Sample Input
2 2
1 2
1 3
2 2
2 1
2 1
2 3
3 2 1
3 2 1
0 0

Sample Output
3
4
6
``````

``````#include<iostream>
using namespace std;

const int N=100;

int a[N+1][N+1]; //a[i][j]=k 代表第i门课上j天所获得的价值为k

int dp[N+1];

//分组背包问题 每组最多拿一个 最典型分组背包
int main()
{
int n,m;

cin>>n>>m;

while(n!=0&&m!=0)
{
int i,j;
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
cin>>a[i][j];
}
}

for(i=1;i<=n;i++)//n门课
{
for(j=m;j>=1;j--)//所拥有的天数
{
for(int k=1;k<=m;k++)//第i门课 的课程时间 每门课有多个课程时间 存储在二维数组a中
{
if(j-k>=0&&dp[j]<dp[j-k]+a[i][k])
{
dp[j]=dp[j-k]+a[i][k];
}
}
}
}

cout<<dp[m]<<endl;
memset(dp,0,sizeof(dp));
cin>>n>>m;
}

return 0;
}
``````

``````I love sneakers!
**Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5718    Accepted Submission(s): 2344**
Problem Description
After months of hard working, Iserlohn finally wins awesome amount of scholarship. As a great zealot of sneakers, he decides to spend all his money on them in a sneaker store.
There are several brands of sneakers that Iserlohn wants to collect, such as Air Jordan and Nike Pro. And each brand has released various products. For the reason that Iserlohn is definitely a sneaker-mania, he desires to buy at least one product for each brand.
Although the fixed price of each product has been labeled, Iserlohn sets values for each of them based on his own tendency. With handsome but limited money, he wants to maximize the total value of the shoes he is going to buy. Obviously, as a collector, he won’t buy the same product twice.Now, Iserlohn needs you to help him find the best solution of his problem, which means to maximize the total value of the products he can buy.

Input
Input contains multiple test cases. Each test case begins with three integers 1<=N<=100 representing the total number of products, 1 <= M<= 10000 the money Iserlohn gets, and 1<=K<=10 representing the sneaker brands. The following N lines each represents a product with three positive integers 1<=a<=k, b and c, 0<=b,c<100000, meaning the brand’s number it belongs, the labeled price, and the value of this product. Process to End Of File.

Output
For each test case, print an integer which is the maximum total value of the sneakers that Iserlohn purchases. Print "Impossible" if Iserlohn's demands can’t be satisfied.

Sample Input
5 10000 31 4 62 5 73 4 991 55 772 44 66

Sample Output
255
``````

dp[i][j] 代表前i个品牌鞋子花费j元 所获得的最大价值

• dp[i][k]是不选择当前鞋子；
• dp[i-1][k-v[j]]+w[j]是选择当前鞋子，但是是第一次在本组中选，由于开始将该组dp赋为了-1，所以第一次取时，必须由上一组的结果推知，这样才能保证得到全局最优解；
• dp[i][k-v[j]]+w[j]表示选择当前鞋子，并且不是第一次在本组中取。

``````#include <iostream>
using namespace std;

const int N=100;

const int Max_brand=10;
const int Max_money=10000;

int s[N+1];// 鞋子的品牌数组
int v[N+1];// 鞋子的价值数组
int c[N+1];// 鞋子的费用数组

int dp[Max_brand+1][Max_money+1]; //dp[i][j]  代表购买前i组品牌鞋子花费j元所得到的最大家价值 一个品牌的鞋子算一个分组

int max(int a,int b,int c)
{
return a>b?(a>c?a:c):(b>c?b:c);
}

//分组背包问题  每组至少取一个

/*
dp[i][k]是不选择当前鞋子；
dp[i-1][k-v[j]]+w[j]是选择当前鞋子，但是是第一次在本组中选，由于开始将该组dp赋为了-1，所以第一次取时，必须由上一组的结果推知，这样才能保证得到全局最优解；
dp[i][k-v[j]]+w[j]表示选择当前鞋子，并且不是第一次在本组中取。
*/

/*

*/

int main()
{
int n,m,S;

while(scanf("%d%d%d",&n,&m,&S)!=EOF)
{

int i,j,si,vi,ci;

for(i=0;i<n;i++)
{
cin>>si>>ci>>vi;
s[i]=si;
c[i]=ci;
v[i]=vi;
}

for(i=0;i<=S;i++)
{
for(j=0;j<=m;j++)
{
if(i==0)
{
dp[i][j]=0;
}
else
{
dp[i][j]=-1;
}
}
}

for(i=1;i<=S;i++)
{
for(j=0;j<=n;j++)//遍历所有鞋子
{
if(s[j]==i)//找到品牌为i的鞋子
{
for(int k=m;k>=c[j];k--)//第i组内选择
{
dp[i][k]=max(dp[i][k],dp[i][k-c[j]]+v[j],dp[i-1][k-c[j]]+v[j]);
}
}
}
}

if(dp[S][m]<0)
{
cout<<"Impossible"<<endl;
}
else
{
cout<<dp[S][m]<<endl;
}
}

return 0;
}
``````