2016.1.27
試題描述
現在一共有4種硬幣,面值各不相同,分別為ci(i=1,2,3,4)。某人去商店買東西,去了tot次,每次帶di枚ci硬幣,購買價值為si的貨物。請問每次有多少種付款方法。
輸入 第一行包括五個數,分別為c1,c2,c3,c4和tot 接下來有tot行,每行五個數,第i+1行五個數依次為第i次購物所帶四種硬幣的數目和購買貨物的價值(d1,d2,d3,d4,s )。各行的數兩兩之間用一個空格分隔。 輸出 一行,包括tot個數,依次為每次付款的方法數。兩數之間用一個空格分隔。 輸入示例 1 2 5 10 2
hzwer給的容斥原理解法
先預處理出達到面值s的方法,不考慮硬幣是否超過限制。這個用簡單背包就好。
然後從裡面扣出有至少一枚硬幣超過限制的方法即是答案。
又有:至少一枚硬幣超限=第一種硬幣超限+第二種硬幣超限+第三種硬幣超限+第四種硬幣超限-第一種和第二種都超限-第一種和第三種都超限-第一種和第四種都超限-第二種和第三種都超限-第二種和第四種都超限-第三種和第四種都超限+......-四種都超限
然後就狀壓容斥
若第一種硬幣超限,則f[s-(d[1]+1)*c[1]]即為方案數,因為保證了多用一次第一種硬幣。以此類推
注意判sum超s的情況
AC代碼:
#include<iostream> using namespace std; int c[5],tot,d[5],s,ct,sum,flag; long long f[100005],ans; int main() { for(int i=1;i<=4;i++) scanf("%d",&c[i]); scanf("%d",&tot); f[0]=1; for(int i=1;i<=4;i++) { for(int j=c[i];j<=100000;j++) { f[j]+=f[j-c[i]]; } } while(tot--) { for(int i=1;i<=4;i++) scanf("%d",&d[i]); scanf("%d",&s); ans=0; for(int i = (1 << 4) - 1 ; i >= 0 ; i-- ) { ct=0;sum=0; for(int j = 3 ; j >= 0 ; j-- ) { if(1<<j & i) ct++,sum+=(d[j+1]+1)*c[j+1]; } if(s<sum) continue; if(ct & 1) ans-=f[s-sum]; else ans+=f[s-sum]; } if(flag) cout<<" "; flag=1; cout<<ans; } } View Code