poj 1276 Cash Machine 混合背包

缘起

还是要继续把背包问题好好总结完毕啊~

学习混合背包 ~ poj 1276 Cash Machine

分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
有各种不同面值的货币,每种面值的货币有不同的数量,请找出利用这些货币可以凑成的最接近且小于等于给定的数
字cash的金额。
例如 N=3, n1=10, D1=100, n2=4, D2=50, n3=5, D3=10 表示有三种不同的面额,100、50、10
分别有10枚、4枚、5枚.

【输入】
多样例. 每行样例的格式是 cash N n1 D1 n2 D2 ... nN DN, 其中
0 <= cash <= 100000
0 <=N <= 10
0 <= nk <= 1000
1 <= Dk <= 1000
k=1,...,N

【输出】
对每个样例, 输出最接近能凑出的币值

【样例输入】
735 3 4 125 6 5 3 350
633 4 500 30 6 100 1 5 0 1
735 0
0 3 10 100 10 50 10 10

【样例输出】
735
630
0
0

【限制】
1s

其实确切讲, 混合背包就是多重背包嘛~ 直接上板子就好了~ 代码没有写注释, 不懂的话, 参见【1】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//#include "stdafx.h"
#include <stdio.h>
#include <ctype.h>
//#define LOCAL

int read(int &x)
{
x = 0;
int f = 1;
char c = getchar();
if (!~c)
{
return -1;
}
while(!isdigit(c))
{
if (c=='-')
{
f = -1;
}
c = getchar();
}
while(isdigit(c))
{
x = (x<<3)+(x<<1)+(c^48);
c = getchar();
}
x*=f;
}

void write(int x)
{
if (x<0)
{
putchar('-');
x = -x;
}
if (x>9)
{
write(x/10);
}
putchar(48+x%10);
}

const int maxn = 15, maxv = 1e5+5;
int n,cash, nn[maxn], dd[maxn];
bool dp[maxv];

void cp(int c) // 对耗费为c的物品做完全背包
{
for (int i = c;i<=cash;i++)
{
dp[i] = dp[i] || dp[i-c]; // 这里体现价值也是c
}
}

void zp(int c) // 最耗费为c的物品做01背包
{
for (int i = cash; i>=c; i--)
{
dp[i] = dp[i] || dp[i-c]; // 这里体现价值也是c
}
}

void mp(int c, int m) // 对一件费用为c,数量为m的物件做多重背包
{
if (c*m>=cash)
{
cp(c);
}
else
{
int k = 1;
while(k<m)
{
zp(k*c);
m-=k;
k<<=1;
}
zp(m*c);
}
}

int main() {
#ifdef LOCAL
freopen("d:\\data.in", "r", stdin);
// freopen("d:\\my.out", "w", stdout);
#endif
while(~read(cash))
{
read(n);
for (int i = 1;i<=n; i++)
{
read(nn[i]), read(dd[i]);
}
for (int i = 0;i<=cash; i++)
{
dp[i] = !i;
}
for (int i = 1;i<=n; i++)
{
mp(dd[i], nn[i]); // 第i件物品的价值是dd[i](本题也属于价值=费用的类型), 有nn[i]件
}
while(cash)
{
if (dp[cash])
{
break;
}
--cash;
}
printf("%d\n", cash);
}
return 0;
}

ac情况

Status Accepted
Time 47ms
Memory 220kB
Length 1389
Lang C++
Submitted 2019-10-25 09:45:32
Shared
RemoteRunId 20988145

参考

【1】https://yfsyfs.github.io/2019/08/28/poj-1014-Dividing-%E5%A4%9A%E9%87%8D%E8%83%8C%E5%8C%85%E4%B9%8B%E4%BA%8C%E8%BF%9B%E5%88%B6%E4%BC%98%E5%8C%96/