HDU 2844 Coins

多重背包问题,用n个物品,每个物品有价值v[i],每个物品数量限制为num[i]。

这道题是问,用所有的硬币能够在m的范围内最多可以组合成多少种价值。

1
2
3
4
5
对于每个硬币而言:
IF 价值×数量>=m THEN
取这个硬币的次数相当于无限制,可以考虑成完全背包
ELSE THEN
考虑成0-1背包(二进制优化),就是把这个硬币的v和num组合出0-1背包可能出现的状态(可以去看背包九讲)

_(对于num,类似于编码。当2^n _<=num/2时:k = 2^n(n=0,1,2,……)表示状态,对应下来就是二进制的某一位数是1,然后还有一个状态就是k>num/2的时候啦,num+1-k,这样下来就可以用k来组合枚举出从1->num的所有可能了。然后对于k,单位价值和大小都乘上k之后就变成了一个0-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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Maxv 100001
#define Maxn 101
int v[Maxn],num[Maxn];
int knap[Maxv];
int n,m;
void MultipleSack(int v,int num)
{
int i,j,k;
int space;
if (v*num >= m)
{
//用完全背包去求
for (space = v; space <= m; space++)
{
knap[space] = knap[space]|knap[space-v];
}
}
//用0-1背包去求,二进制优化
for (k = 1; k<=num/2; k=(k<<1))
{
for (space = m; space >= k*v; space--)
knap[space] = knap[space]|knap[space-k*v];
}
k = num+1-k;
for (space = m; space >= k*v; space--)
knap[space] = knap[space]|knap[space-k*v];
return ;
}
int main()
{
freopen("Sample Input.txt","r",stdin);
int i,j,k,t;
while (scanf("%d%d",&n,&m),n&&m)
{
for (i = 1; i <= n; i++)
scanf("%d",&v[i]);
for (i = 1; i <= n; i++)
scanf("%d",&num[i]);
memset(knap,0,sizeof(knap));
knap[0] = 1;
for (i = 1; i <= n; i++)
MultipleSack(v[i],num[i]);
for (i = 1,t = 0; i <= m; i++)
t += knap[i];
printf("%d\n",t);
}
getch();
return 0;
}