hihocoer系列
p1364 奖券兑换
题目:
描述
小Hi在游乐园中获得了M张奖券,这些奖券可以用来兑换奖品。
可供兑换的奖品一共有N件。第i件奖品需要Wi张奖券才能兑换到,其
价值是Pi。
小Hi使用不超过M张奖券所能兑换到的最大奖品总价值是多少?
输入
第一行两个整数N,M。
接下来N行,每行两个整数Wi,Pi。
对于 50%的数据: 1≤N,M≤1000
对于 100%的数据: 1≤N,M≤10e5,1≤Pi,Wi≤10。
输出
一行一个整数,表示最大的价值。
分析:
0-1背包问题,时间复杂度O(NM),空间复杂度O(NM)
for(int i = 0;i <= M;i ++)
dp[0][i] = 0;
for(int i = 1;i <= N;i ++)
for(int j = 0;j <= M;j ++)
dp[i][j] = max(dp[i-1][j], dp[i-1][j-W[i]]+P[i]);
0-1背包问题,空间复杂度优化O(M)
for(int i = 0;i <= M;i ++)
dp[i] = 0;
for(int i = 1;i <= N;i ++)
for(int j = M;j >= 0;j --)
dp[j] = max(dp[j], dp[j-W[i]]+P[i]);
由于1≤Pi,Wi≤10这个条件,此题0-1背包问题可以转换为多重背包问题
多重背包问题可以采用二进制优化。
定义多重背包问题为:
N件物品,第i件物品重量为Wi,价值为Pi,数量为Ci。求容量为M的背包可取物品的最大价值。
O(NMK)(K为物品的最大数量)的写法
for(int i = 0;i <= M;i ++)
dp[0][i] = 0;
for(int i = 1;i <= N;i ++)
for(int j = 0;j <= M;j ++)
for(int k = 0;k <= C[i];k ++)
dp[i][j] = max(dp[i][j], dp[i-1][j-W[i]*k]+P[i]*i);
二进制优化:
设t = logCi ,数量Ci可分解为 20+21+...+2t+(Ci−20−21−...−2t)
第i件物品应该取走的数目可表示为 b0∗20+b1∗21+...+bt∗2t+b(t+1)∗a(Ci−20−21−...−2t)
其中b为二进制变量
因此第i件物品的最佳取法变成了b的最佳取值,这又变成了0-1背包问题
转换过程具体代码如下
int count = 0;
int bitP[maxm];
int bitW[maxm];
for(int i = 1;i <= N;i ++){
int k;
for(k = 1;(k<<1) <= C[i];k <<= 1){
bitP[count] = P[i]*k;
bitW[count] = W[i]*k;
count ++;
}
bitP[count] = P[i]*(C[i]-k+1);
bitW[count] = W[i]*(C[i]-k+1);
count ++;
}
代码
#include <iostream>
#include <stdio.h>
using namespace std;
#define maxm(a, b) ((a)>(b)?(a):(b))
#define MAXW 10
#define MAXP 10
#define MAXCOUNT 2000
#define MAXM 100000
int ticket[MAXW][MAXP];
int W[MAXCOUNT];
int P[MAXCOUNT];
int dp[MAXM + 1];
int n, m, count;
int main()
{
cin>>n>>m;
for(int i = 0;i < MAXW;i ++)
for(int j = 0;j < MAXP;j ++)
ticket[i][j] = 0;
int w, p;
for(int i = 0;i < n;i ++){
scanf("%d %d", &w, &p);
ticket[w-1][p-1] ++;
}
int k, count = 0;
for(int i = 0;i < MAXW;i ++){
for(int j = 0;j < MAXP;j ++){
for(k = 1;(k<<1) <= ticket[i][j]; k <<= 1){
W[count] = (i+1)*k;
P[count] = (j+1)*k;
count ++;
}
W[count] = (i+1)*(ticket[i][j] - k + 1);
P[count] = (j+1)*(ticket[i][j] - k + 1);
count ++;
}
}
for(int i = 0;i <= m;i ++)
dp[i] = 0;
for(int i = 0;i < count;i ++){
for(int j = m;j >= 0;j --){
if(j >= W[i]){
dp[j] = maxm(dp[j], dp[j-W[i]] + P[i]);
}
}
}
cout<<dp[m]<<endl;
return 0;
}