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 */