HDOJ的这道题比较水,POJ的题好牛
N - Coins
Time Limit:1000MS MemoryLimit:32768KB 64bit IO Format:%I64d& %I64u
Submit Status
Description
Whuacmers usecoins.They have coins of value A1,A2,A3...An Silverland dollar. One day Hibixopened purse and found there were some coins. He decided to buy a very nicewatch in a nearby shop. He wanted to pay the exact price(without change) and heknown the price would not more than m.But he didn't know the exact price of thewatch.
You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cncorresponding to the number of Tony's coins of value A1,A2,A3...An thencalculate how many prices(form 1 to m) Tony can pay use these coins.
Input
The inputcontains several test cases. The first line of each test case contains twointegers 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 lasttest case is followed by two zeros.
Output
For each testcase 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
HDOJ版:
一开始根本想不到优化,之后上网搜题解发现什么二进制优化,学习了。之后就是将多重背包转化为完全或01背包。代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int T = 0x3f3f3f3f;
struct S
{
int val,sum;
};
int dp[100001];
void INIT(int k)
{
for(int i=0;i<=k;++i)
{
dp[i]=T;
}
dp[0]=0;
}
int main()
{
S a[110];
int n,m,i,j,k;
while(scanf("%d%d",&n,&m)&&(n&&m))
{
INIT(m);
for(i=0;i<n;++i)
{
scanf("%d",&a[i].val);
}
for(i=0;i<n;++i)
{
scanf("%d",&a[i].sum);
}
for(i=0;i<n;++i)
{
if(a[i].val*a[i].sum>=m)
{
for(j=a[i].val;j<=m;++j)
{
dp[j]=min(dp[j],dp[j-a[i].val]+1);
}
}
else
{
k=1;
while(k<a[i].sum)
{
for(j=m;j>=k*a[i].val;--j)
{
dp[j]=min(dp[j],dp[j-k*a[i].val]+1);
}
a[i].sum-=k;//每进行一次就就要改变数量方便知道是否用完
k*=2;
}
if(a[i].sum)
{
for(j=m;j>=a[i].sum*a[i].val;--j)
{
dp[j]=min(dp[j],dp[j-a[i].sum*a[i].val]+1);
}
}
}
}
for(i=1,k=0;i<=m;++i)
{
if(dp[i]!=T)
{
k++;
}
}
printf("%d\n",k);
}
return 0;
}
POJ版:
这题消掉第三个循环挺难想到的,如果没看别人的题解根本想不出要怎麽做。其实就是利用一个cnt数组将用了多少个物品的个数记录下来,而物品的使用又与动态转移时的前一个状态dp[i-1][j-a[i]。val]有关,所以cnt的使用次数也是这个状态的转移方程。即是这个题目一共使用了两个状态转移方程,一个是dp价值,一个是cnt次数,他们是紧密联系在一起的。只能一起转移或都不转移两种。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int T = 0x3f3f3f3f;
int dp[100100],cnt[100100];
void INIT(int m)
{
for(int i=0;i<=m;++i)
{
dp[i]=T;
}
dp[0]=0;
}
struct S
{
int val,sum;
}a[110];
int main()
{
int n,m,i,j,k;
while(scanf("%d%d",&n,&m)&&(m+n))
{
INIT(m);
for(i=0;i<n;++i)
{
scanf("%d",&a[i].val);
}
for(i=0;i<n;++i)
{
scanf("%d",&a[i].sum);
}
for(i=0,k=0;i<n;++i)
{
memset(cnt,0,sizeof(cnt));
for(j=a[i].val;j<=m;++j)
{
if(dp[j]==T&&!dp[j-a[i].val]&&cnt[j-a[i].val]<a[i].sum)
/*
dp[j]=未组合的数dp[j-a[i]。val]=组合了的数
cnt[j-a[i].val]=当前组合用了多少个个数
*/
{
dp[j]=0;
cnt[j]=cnt[j-a[i].val]+1;
k++;//符合条件的加一
}
}
}
printf("%d\n",k);
}
return 0;
}