Coins
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 12746 Accepted Submission(s): 5116
Problem Description
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.
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.
Input
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.
Output
For each test case output the answer on a single line.
Sample Input
3 10 1 2 4 2 1 1 2 5 1 4 2 1 0 0
Sample Output
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>
感谢这个博主,贴一下他的这篇博客网址: