1042: [HAOI2008]硬币购物
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1301 Solved: 762
[ Submit][ Status][ Discuss]
Description
硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次有多少种付款方法。
Input
第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s
Output
每次的方法数
Sample Input
1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900
3 2 3 1 10
1000 2 2 2 900
Sample Output
4
27
27
HINT
数据规模
di,s<=100000
tot<=1000
步骤1:预处理出完全背包情况下所有大小背包的总方案数。
步骤2:完全背包算必然多加了好多方案,所以需要减去第i种多出d[i]个钱币的方案数,但还需要加上(i,j)同时多出的方案数..........................
步骤3:假设求第i种钱币多出的方案数。我们只要先固定取出d[i]+1个i钱币,剩余的钱币随意取就可以了。即ans -= dp[s-(d[i]+1)*c[i]]。当然前提条件是s-(d[i]+1)*c[i]>=0
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define N 100009
typedef long long LL;
LL dp[N],ans;
int c[5],d[5];
int s;
struct Stack {
LL sum,k;
Stack(){}
Stack(LL _sum,LL _k){
sum = _sum; k = _k;
}
}stack[20];
void init(){
memset(dp,0,sizeof(dp));
dp[0] = 1;
for(int i = 1;i<=4;i++)
for(int k = c[i];k<N;k++)
dp[k] += dp[k-c[i]];
}
void solve() {
int top = 0;
stack[top++] = Stack(0,0);
for(int i = 1;i<=4;i++) {
LL t = (LL)(d[i]+1)*c[i];
int tem = top;
for(int j = 0;j<tem;j++) {
LL sum = stack[j].sum,res = 0;
LL k = stack[j].k;
if(s-(sum+t)>=0) res = dp[s-(sum+t)];
else break;
if(k%2==0) ans -= res;
else ans += res;
stack[top++] = Stack(sum+t,k+1);
}
}
}
int main(){
int cas;
//freopen("Test.txt","r",stdin);
cin>>c[1]>>c[2]>>c[3]>>c[4]>>cas;
init();
while(cas--){
cin>>d[1]>>d[2]>>d[3]>>d[4]>>s;
ans = dp[s];
solve();
cout<<ans<<endl;
}
return 0;
}