bzoj 1042 HAOI2008 硬币购物

    这道题思路是在是神。

    先dp出没有限制时候的方案数。

    dp的时候注意 先循环 1..4 再循环 1..maxs 防止重复。边界是f[0] = 1。 这么基础的背包都忘记了=_=

    接下来处理有重复的问题,容斥原理

    容斥原理说起来很简单,但有一些很神奇的应用,比如这道题。

    最终的答案 = 没有限制的方案 - 其中一种超了限制的方案 + 其中两种超了限制的方案 - 三种超了限制的方案 + 四种超了限制的方案

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

    为什么是 d[i]+1 呢?

    至少用d[i]+1个,剩下的随意,又是不限制的方案数了。

    上代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100010
using namespace std;

int c[5], n, d[5], s;
long long f[N] = {0};

void make_f()
{
    f[0] = 1;
    for (int j = 1; j <= 4; ++j)
        for (int i = c[j]; i <= N-10; ++i)
            f[i] += f[i-c[j]];
}

long long getans()
{
    long long ans = f[s];
    for (int i = 1; i <= 4; ++i)
        if (s - c[i]*(d[i]+1) >= 0)
            ans -= f[s - c[i]*(d[i]+1)];
    for (int i = 1; i <= 3; ++i)
        for (int j = i+1; j <= 4; ++j)
            if (s - c[i]*(d[i]+1) - c[j]*(d[j]+1) >= 0)
                ans += f[s - c[i]*(d[i]+1) - c[j]*(d[j]+1)];
    for (int i = 1; i <= 2; ++i)
        for (int j = i+1; j <= 3; ++j)
            for (int k = j+1; k <= 4; ++k)
                if (s - c[i]*(d[i]+1) - c[j]*(d[j]+1) - c[k]*(d[k]+1) >= 0)
                    ans -= f[s - c[i]*(d[i]+1) - c[j]*(d[j]+1) - c[k]*(d[k]+1)];
        if (s - c[1]*(d[1]+1) - c[2]*(d[2]+1) - c[3]*(d[3]+1) - c[4]*(d[4]+1) >= 0)
            ans += f[s - c[1]*(d[1]+1) - c[2]*(d[2]+1) - c[3]*(d[3]+1) - c[4]*(d[4]+1)];
    return ans;
            
}

int main()
{
    for (int i = 1; i <= 4; ++i) scanf("%d", &c[i]);
    make_f(); scanf("%d", &n);
    while (n--)
    {
        for (int i = 1; i <= 4; ++i) scanf("%d", &d[i]);
        scanf("%d", &s);
        printf("%I64d\n", getans());
    }
    return 0;
}

 

posted @ 2014-10-05 10:59  handsomeJian  阅读(196)  评论(0编辑  收藏  举报