HUD 2844 coins

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.
 

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
 

Statistic |  Submit |  Discuss |  Note

题意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<stdio.h>
#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了这道题,收获良多啊,慢慢消化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值