1. 程式人生 > >BZOJ 1042 [HAOI2008]硬幣購物(完全揹包+容斥)

BZOJ 1042 [HAOI2008]硬幣購物(完全揹包+容斥)

題意:

4種硬幣買價值為V的商品,每種硬幣有numi個,問有多少種買法

1000次詢問,numi<1e5

思路:

完全揹包計算出沒有numi限制下的買法,

然後答案為dp[V]-(s1+s2+s3+s4)+(s12+s13+s14+s23+s24+s34)-(s123+s124+s134+s234)+s1234
其中s...為某硬幣超過限制的方案數
求s的方法:
如s1:硬幣1超過限制,就是硬幣1至少選了num1+1個,其他隨便,所以s1=dp[V-c1*(num1+1)]
同理s12 = dp[V - c1 * (num1 + 1) - c2 * (num2 + 1)]
程式碼:
#include<iostream>
#include
<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<string> #include<stack> #include<queue> #include<deque> #include<set> #include<vector> #include<map> #include<functional> #define fst first #define
sc second #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define lson l,mid,root<<1 #define rson mid+1,r,root<<1|1 #define lc root<<1 #define rc root<<1|1 #define lowbit(x) ((x)&(-x)) using namespace std; typedef double db; typedef long double
ldb; typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> PI; typedef pair<ll,ll> PLL; const db eps = 1e-6; const int mod = 1e9+7; const int maxn = 2e6+100; const int maxm = 2e6+100; const int inf = 0x3f3f3f3f; const db pi = acos(-1.0); ll dp[maxn]; ll c[5]; ll v[maxn]; ll num[5]; ll ans,V; //dfs搜容斥組合 void dfs(int x, int k, ll sum){//搜到第x個,已經選了k個,當前組合一共需要減sum //printf("%d %d %lld\n",x,k,sum); if(V-sum < 0)return; if(x==5){ //容斥判斷該加還是減 if(k==0)return; if(k&1) ans += dp[V-sum]; else ans -= dp[V-sum]; return; } dfs(x+1, k, sum);//當前不選 dfs(x+1,k+1,sum+c[x]*(num[x]+1));// } int main(){ for(int i = 1; i <= 4; i++){ scanf("%lld", &c[i]); } int T; scanf("%d", &T); dp[0] = 1; for(int i = 1; i <= 4; i++){ for(int j = 0; j <= maxn; j++){ if(j-c[i]>=0)dp[j] += dp[j-c[i]]; } } while(T--){ for(int i = 1; i <= 4; i++){ scanf("%lld", &num[i]); } scanf("%lld", &V); ans = 0; dfs(1, 0, 0); printf("%lld\n",dp[V]-ans); } return 0; } /* 1 2 5 10 1 3 2 3 1 10 */

 

[HAOI2008]硬幣購物否