您的位置:首页 > 其它

hdu2844 多重背包+二进制优化(多重背包的完全背包优化解法)

2016-10-12 18:40 405 查看

                                                         Coins

[b]                                                 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

                                                                  Total Submission(s): 12746    Accepted Submission(s): 5116
[/b]

[align=left]Problem Description[/align]
Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. One day Hibix opened purse and found there were some coins. He decided to buy a very nice watch in a nearby shop. He wanted
to pay the exact price(without change) and he known the price would not more than m.But he didn't know the exact price of the watch.

You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins.
 

[align=left]Input[/align]
The input contains several test cases. The first line of each test case contains two integers n(1 ≤ n ≤ 100),m(m ≤ 100000).The second line contains 2n integers, denoting A1,A2,A3...An,C1,C2,C3...Cn (1
≤ Ai ≤ 100000,1 ≤ Ci ≤ 1000). The last test case is followed by two zeros.

 

[align=left]Output[/align]
For each test case output the answer on a single line.
 

[align=left]Sample Input[/align]

3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0

 

[align=left]Sample Output[/align]

8
4

 
题意:给你n种钱,每种钱有Cn张,问你最多能组成多少种钱数。
思路:好开心的一道题,昨晚就在想多重背包的二进制优化这个问题,结果今天就做到了。o(^▽^)o。这个本来是一个非常简单的多重背包问题,看一下if (dp[ i - money[j] ] == 1)  dp[i] = 1;但是他给的数据太大了,1e5的钱,有100种钱,每种钱1000张,这样的话复杂度就变成了1e10,显然会TLE,所以我们用二进制对钱的数量进行优化,那1000张,可以化为2*log2(1000),不到20,这样的话,复杂度就到了1e8,勉强能过。。。不过我在做出来以后看他们的题解,发现有的人用了另一种方法,他把这个题当作完全背包设了一个use[i]的数组,代表
i 元用了多少张这种钱,每放一种钱,他就memset一遍,这样的话,他的复杂度只有1e7。这很ACM。
贴一下二进制优化多重背包的代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int a[105][15];
int dp[100050];
int value[105];
void bt(int re, int n){
int number = 0;
int k = 1;
while(n >= k){
number++;
n -= k;
a[re][number] = k;
k = k << 1;
}
while(n > 0){
number++;
while(k > n)
k = k >> 1;
n -= k;
a[re][number] = k;
}
a[re][0] = number;
}
int main(){
int n,m,temp;
while(scanf("%d%d",&n,&m) && n+m){
memset(dp,0,sizeof(dp));
for(int i = 0; i < n; i++)
scanf("%d",&value[i]);
for(int i = 0; i < n; i++){
scanf("%d",&temp);
bt(i,temp);
}
dp[0] = 1;
for(int i = 0; i < n; i++)
for(int j = 1; j <= a[i][0]; j++)
for(int k = m; k >= a[i][j]*value[i]; k--)
if(dp[k-a[i][j]*value[i]] == 1)dp[k] = 1;
int number = 0;
for(int i = 1; i <= m; i++)
if(dp[i])number++;
cout << number <<endl;
}
}


完全背包加use数组的解法:
#include <stdio.h>
#include <string.h>

bool dp[100010];
int use[100010];//i元钱时某种钱用的次数
int n,m;
int val[110],num[110];

void solve()
{
memset(dp,0,sizeof(dp));
dp[0] = 1;
int count = 0;
for(int i = 1; i <= n; i++)
{
memset(use,0,sizeof(use));  //每次初始化第i种钱用了0次
for(int j = val[i]; j <= m; j++)//顺序枚举钱数
{
if(dp[j-val[i]] && !dp[j] && use[j-val[i]] < num[i])
{
dp[j] = 1;
use[j] = use[ j-val[i] ]+1;//到达j元用的i种钱的次数是到达 j-val[i]元用的次数加1
count++;
}
}
}
printf(%d
,count);
}
int main()
{
while(~scanf(%d %d,&n,&m))
{
if(n == 0 && m == 0)
break;

for(int i = 1; i <= n; i++)
scanf(%d,&val[i]);

for(int i = 1; i <= n; i++)
scanf(%d,&num[i]);

solve();

}
return 0;

}
</string.h></stdio.h>


感谢这个博主,贴一下他的这篇博客网址:
点击打开链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: