BZOJ 1042: [HAOI2008]硬币购物 DP,与处理,容斥

来源:互联网 发布:广数g92车英制螺纹编程 编辑:程序博客网 时间:2024/05/22 13:05

Description

  硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买s
i的价值的东西。请问每次有多少种付款方法。
Input

  第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s,其中di,s<=100000,tot<=1000
Output

  每次的方法数
Sample Input
1 2 5 10 2

3 2 3 1 10

1000 2 2 2 900
Sample Output
4

27
解题方法: 容斥好题,首先不考虑数量限制,dp[i][j]表示点 i 种硬币组成价值为 j 的物品的方案数,则有 dp[0][0]=0,dp[i][j]=dp[i
-1][j]+dp[i][j-c[i],最终答案就是 dp[4][n]。我们考虑个数限制,则答案应为任何一种都无限制的方案,减去
第一种硬币个数超了的方案,减去第二种硬币个数超了的方案,减去第三种硬币个数超了的方案,减去第四种硬
币个数超了的方案,加上第一种,第二种硬币个数超了的方案,……,加上第一种,第二种,第三种,第四种硬
币个数超了的方案,总之就是个容斥原理。我们考虑如何计算第一种硬币个数超了的方案,设面值为 c,数量为
d,则我们必须要用第一种硬币选出(d+1)×c 的价值,然后剩下的 n-(d+1)×c 随便选,因此第一种硬币个数超
了的方案就是 dp[4][n-(d+1)×c],剩下情况类似,这样每次询问的复杂度就做到了 O(1)。

代码如下:

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int S = 100010;#define rep(i, a, b) for(int i = a; i <= b; i++)int c[5], d[5];int n;LL f[S], ans;void dfs(int x, int k, int sum) //{    if(sum < 0) return ;    if(x == 5){        if(k & 1) ans -= f[sum];        else ans += f[sum];        return ;    }    dfs(x + 1, k + 1, sum - (d[x] + 1) * c[x]);    dfs(x + 1, k, sum);}int main(){    scanf("%d%d%d%d%d", &c[1], &c[2], &c[3], &c[4], &n);    f[0] = 1;    rep(i, 1, 4){        rep(j, c[i], S - 1){            f[j] += f[j - c[i]];        }    }    rep(i, 1, n){        int x;        scanf("%d%d%d%d", &d[1], &d[2], &d[3], &d[4]);        scanf("%d", &x);        ans = 0;        dfs(1, 0, x);        printf("%lld\n", ans);    }    return 0;}
0 0
原创粉丝点击