Coins
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4196 Accepted Submission(s): 1669
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
Source
Recommend
gaojie
题意A[i]种硬币,每种C[i]个,求解能够形成多少种小于m的不同的面值。
等价于A[i]种物品,每种C[i]个,求解能装满多少小于m的不同背包。
注意不要用对1到m的背包进行循环,这样做只会各种TLE,POJ的3秒都卡不过。
可以对物品的种类进行考虑。考虑每种物品能够去装满哪些背包。
用f数组表示背包,0表示未装满,1表示装满。
如果A[i]*C[i]>=m,则可以转化为多重背包问题。否则可以用01背包问题求解,用二进制去优化。
AC代码(内含解析):
#include<math.h>
#define N 100001
int a[N],c[N],b[1001];//b数组用来存放二进制优化后的物品所占空间
int m,f[N];
void zeropack(int h)
{
int i,j,k;
for(k=0;c[h]-(int)pow(2,k)+1>0;k++);k--;
for(i=0;i<k;i++)
b[i]=a[h]*(int)pow(2,i);
b[k]=a[h]*(c[h]-(int)pow(2,k)+1);//二进制优化
for(i=k;i>=0;i--)//对转换后的k+1种物品进行选择
{
for(j=m;j>=b[i];j--)//01背包问题,只考虑装或不装
f[j]=f[j]|f[j-b[i]];//用了一个位或运算符,可以简化代码
}
}
void compeletpack(int h)
{
int j;
for(j=a[h];j<=m;j++)//由于可以装无穷多个,则第1个价值为a[h]的包肯定能装满,而后只需考虑要不要再装一个
f[j]=f[j-a[h]]|f[j];
}
int main()
{
int i,num,n;
scanf("%d%d",&n,&m);
while(n||m)
{
num=0;
f[0]=1;
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
for(i=1;i<=n;i++)
scanf("%d",&c[i]);
for(i=1;i<=m;i++)//初始化
f[i]=0;
for(i=1;i<=n;i++)//对n种物品的选择
{
if(a[i]*c[i]>=m)
compeletpack(i);//对于总价值超过m的硬币,可以认为是可取无限多种
else
zeropack(i);
}
for(i=1;i<=m;i++)
num+=f[i];
printf("%d\n",num);
scanf("%d%d",&n,&m);
}
return 0;
}
对于上面的二进制优化还有一种更简洁的写法,可以不用到数组b:
for(k=1;k<=a[h]/2;k=(k<<1))
{
for(j=m;j>=k*a[h];j--)
f[j]=f[j]|f[j-k*a[h]];
}
k=a[h]+1-k;
for(j=m;j>=k*a[h];j--)
{
f[j]=f[j]|f[j-k*a[h]];
}
A了这道题,收获良多啊,慢慢消化。