DP 硬幣購物

2021-08-19 09:10:02 字數 1821 閱讀 8708

【題意】

一共有4種硬幣。面值分別為c1,c2,c3,c4。某人去商店買東西,去了tot次。每次帶di枚面值為ci的硬幣,買si的價值的東西。請問每次有多少種付款方法。

【思路】

設f[i]為不考慮硬幣限制下,買價值為i的物品的方案數。那麼很容易得出f[j]=f[j-c[1]]+f[j-c[2]]…的遞推式。注意要推完一種硬幣再推下一種硬幣。

for(int i=0;i

<=100000;i++)

for(int j=1;j

<=4;j++)

f[i+c[j]]+=f[i];

例如這樣的推法就是錯的。假設我們的四種硬幣面值為2,3,100,1000,那麼按照這個推法,f[5]=f[2]+f[3],(2,3)與(3,2)被重複計數了。實際上的重複情況複雜的多,我們不可能用/2等方法解決它。因此,要推完一種硬幣再推下一種硬幣。

for(int i=1;i

<=4;i++)

for(int j=0;j

<=100000-c[i];j++)

f[j+c[i]]+=f[j];

這樣的方法就是對的。

對於每次詢問,我們先假設答案為f[s],但s>=(d[1]+1)*c[1],即f[s]中包含了第一種硬幣超過限制的情況。我們要減去這部分。

很明顯,對於第一種硬幣超過限制的情況,所用的第一種硬幣數量》=d[1]+1。

容易發現,總和為s,第一種硬幣至少d[1]+1的情況個數,等於總和為s-(d[1]+1)*c[1],硬幣個數沒有限制的情況個數!

因為總和為s,第一種硬幣至少d[1]+1的情況個數中,那d[1]+1個硬幣是固定的,是不會變的,對於情況個數是不會有影響的。

所以,我們將原先為f[s]的答案減去f[s-(d[1]+1)*c[1]]即可得出正確答案。

這只是一種硬幣超過限制的解決辦法。對於兩種硬幣超過限制我們也同樣這麼減去,但重複減去了一部分:那些兩種硬幣同時超過限制的情況。

因此我們要加回兩種硬幣同時超過限制的情況。根據上面的式子,很容易就知道這樣做就是

if(s>=(d[1]+1)*c[1]+(d[2]+1)*c[2])//說明f[s]包含了兩種同時超過限制的情況

ans+=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]]

然後這樣加回去又會多加了三種超過限制的,所以三種的也要減去。然後三種的又會多減了四種的,所以四種要加回。

【**】

#include

#include

#include

#include

#include

#define maxn 110000

#define ll long long

using namespace std;

ll read()

while(x>='0'&&x

<='9')

return ans*f;

}int

m;ll c[5],d[5],f[maxn];

int main()

for(int i=1;i<=2;i++)

for(int j=i+1;j<=3;j++)

for(int k=j+1;k<=4;k++)

if(x>=d[1]*c[1]+d[2]*c[2]+d[3]*c[3]+d[4]*c[4])

ans+=f[x-d[1]*c[1]-d[2]*c[2]-d[3]*c[3]-d[4]*c[4]];

printf("%lld\n",ans);

}return

0;}

bzoj1042 DP 容斥原理 硬幣購物

description 硬幣購物一共有4種硬幣。面值分別為c1,c2,c3,c4。某人去商店買東西,去了tot次。每次帶di枚ci硬幣,買si的價值的東西。請問每次有多少種付款方法。input 第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s output 每次的方法...

HAOI2008 硬幣購物(容斥 揹包DP)

題目位址 先跑一遍完全揹包,然後對於第 i 種硬幣只能用 d i 枚容斥一下。具體的 強制 k 個硬幣超出限制 sum 1 k sum f s c i d i 1 其中,d 是 4 種硬幣構成的集合,f i 是完全揹包後,i 體積有多少種湊成方式 時間複雜度 o ns talk is cheap.s...

BZOJ 1042 硬幣購物

1042 haoi2008 硬幣購物 time limit 10 sec memory limit 162 mb description 硬幣購物一共有4種硬幣。面值分別為c1,c2,c3,c4。某人去商店買東西,去了tot次。每次帶di枚ci硬幣,買s i的價值的東西。請問每次有多少種付款方法。i...