【題意】
一共有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...