硬币方案【ybt高效进阶5-1-4】
题目大意:
给你很多个数,每个数有一定的数量。
问你1~M中有多少个数可以被这堆数中任意几个的和拼成
思路:
设pd[j]表示i是否可以被拼成,dp[i][j]表示拼成j需要用到几个a[i]
因为每个数互相独立,所以dp可以用个一维数组,对于每个a[i],在开始前memset一次即可
那么对于每一次转移我们有:
if(!pd[j]&&dp[j-a[i]]+1<=c[i]&&pd[j-a[i]])
//!pd[j] 判重
//dp[j-a[i]+1<=c[i] 用a[i]的数量不超过c[i]
//pd[j-a[i]] //j-a[i]可以被拼成
{
dp[j]=dp[j-a[i]]+1;
pd[j]^=1;
++ans;//ans是最终答案
}
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#define r register
#define rep(i,x,y) for(r ll i=x;i<=y;++i)
#define per(i,x,y) for(r ll i=x;i>=y;--i)
using namespace std;
typedef long long ll;
const ll V=1e5+10;
ll n,m,a[V],c[V],dp[V],ans;
bool pd[V];
int main()
{
while(true)
{
scanf("%lld%lld",&n,&m);
if(n==0&&m==0) break ;
memset(pd,false,sizeof(pd));
rep(i,1,n) scanf("%lld",&a[i]);
rep(i,1,n) scanf("%lld",&c[i]);
pd[0]=true;
rep(i,1,n)
{
memset(dp,0,sizeof(dp));
rep(j,a[i],m)
if(!pd[j]&&dp[j-a[i]]+1<=c[i]&&pd[j-a[i]])
{
dp[j]=dp[j-a[i]]+1;
pd[j]^=1;
++ans;
}
}
printf("%lld\n",ans);
ans=0;
}
return 0;
}