HAOI 硬幣購物

試題描述: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 900
get

輸出示例: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);
    }
}
相關文章
相關標籤/搜索