試題描述:ios
如今一共有4種硬幣,面值各不相同,分別爲ci(i=1,2,3,4)。某人去商店買東西,去了tot次,每次帶di枚ci硬幣,購買價值爲si的貨物。請問每次有多少種付款方法。git
輸入:數組
第一行包括五個數,分別爲c1,c2,c3,c4和tot 接下來有tot行,每行五個數,第i+1行五個數依次爲第i次購物所帶四種硬幣的數目和購買貨物的價值(d1,d2,d3,d4,s )。各行的數兩兩之間用一個空格分隔。spa
輸出:code
tot行,依次爲每次付款的方法數。blog
輸入示例:ci
1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900get
輸出示例:it
4io
27
數據範圍:
0<di,s<=100000,0<tot<=1000。
這道題其實就是一個dp+容斥原理(不知道什麼是容斥原理的本身上網百度去)……
首先咱們先對於dp數組進行初始操做。咱們定義:dp[i]是在不考慮硬幣是否超限的狀況下用硬幣湊i元的方案數。這樣咱們就能夠獲得狀態方程:dp[j]+=dp[j-c[i]](由數據發現咱們的0<=j<=100000,1<=i<=4)注意:dp[0]=1
而後咱們就能夠進行容斥原理的操做。對於每種硬幣,都有超和不超兩種狀況,因此最終咱們只須要統計2^4=16次就夠了。在記錄狀態的時候,咱們能夠用10進制的數來記錄,但是在操做的時候實際上是對2進制進行操做。舉個例子:好比我用5記錄了一種狀態,5的二進制就是0101,其表達的意思就是第一種和第三種硬幣超出了限度。
那麼咱們應該如何來表示使用硬幣超過了限度?舉個例子:好比當前第i種硬幣有d[i]枚硬幣能夠用的話,若是咱們用到了d[i]+1枚硬幣那就是說咱們用硬幣超過了限度,且其餘硬幣是能夠隨意使用的,因此這樣的狀況應該有dp[s-c[i]*(d[i]+1)]種,若是s-c[i]*(d[i]+1)<0那方案數也就是0。其他的狀況也相似。
這題是要開long long的,要不過不去……我就是這麼死的……
AC代碼:
#include<iostream> #include<memory.h> #include<stdio.h> #include<cstdio> #include<cctype> using namespace std; //-------------------------- void read(long long &x){ x=0;char ch=getchar();long long f=1; for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-'0'; x*=f; } //--------------------------- long long c[5],d[5],s,tot,ans,cnt,sum,cur; long long dp[100000+10]; bool flag=0; int main(){ for(int i=1;i<=4;i++){ read(c[i]); } read(tot); dp[0]=1; for(int i=1;i<=4;i++){ for(int j=c[i];j<=100000;j++)dp[j]+=dp[j-c[i]]; } while(tot--){ for(int i=1;i<=4;i++)read(d[i]); read(s); ans=0; for(int i=0;i<16;i++){ cnt=0;sum=0;cur=0; int t=i; while(t>0){ cur++; if(t&1)sum+=(d[cur]+1)*c[cur],cnt++; t>>=1; } if(s<sum)continue; if(cnt&1)ans-=dp[s-sum]; else ans+=dp[s-sum]; } printf("%lld\n",ans); } }