bzoj1042_The principle of recursive addition of tolerance and exclusion

Another question of the principle of inclusion and exclusion.

When I first watched it, I would only play violence. I thought of a normal dp, but I couldn't think of it, so I had the cheek to read the solution. I've been doing this kind of thing all the time recently, and after reading the solution, it feels very simple... The topic of the principle of inclusion and exclusion is still unfamiliar. Of course, when reading the solution, I prefer those with detailed proofs, because I would be very tired if I didn't prove it (I'm so lazy).

The key point of this question is naturally the principle of inclusion and exclusion. For the use of four coins, if we remember that the number of i overused solutions is g[i], then the answer ans = g[4] - g[3 ] + g[2] - g[1], which is directly obtained by the principle of inclusion and exclusion. All that's left is to find g[i]. In fact, this is an abstract process, and we can finally use recursion to implement it, instead of seeking the g[] array. Might as well set the first type of coins to be overused, even if the amount exceeds lim[1], then the rest can be taken arbitrarily, that is to say, the number of situations at this time and the unlimited sum-(c[1]*(lim The number of cases for [1]+1)) is the same, because once more than d[1] is used, the other cases don't matter, and the same is true for other types of coins. As for the situation when there is no limit, I think it can be calculated quickly with recursion.

Construct a search framework to record that several coins have used excessive st before. From the expression of ans at the beginning, we can know that when st is an odd number, ans needs to subtract this number, otherwise add this number, so it is out of bounds. After the decision, we need to invert the st variable, so that we get the dfs function in the code.

#include <cstdio>
#define N 100000 + 10

using namespace std;

typedef long long LL;
int m, t, c[4],  lim[4];
LL f[N], ans;
void first()
{
    f[0] = 1;
    for (int i = 0; i < 4; ++i)
    for (int j = c[i]; j <= 100000; ++j)
    f[j] += f[j-c[i]];//递推求无限制时的方案数
}
void init()
{
    for (int i = 0; i < 4; ++i)
    scanf("%d", &lim[i]);
    scanf("%d", &m);
}
void dfs(int x, int sum, int st)
{
    if (sum < 0) return;//不存在的情况
    if (x == 4)
    {
        if (st & 1) ans -= f[sum];
        else ans += f[sum];//st代表是奇数过量还是偶数过量
        return;
    }
    dfs(x+1, sum, st);
    dfs(x+1, sum-(c[x]*(lim[x]+1)), !st);//进行当前硬币过量的决策
}
void deal()
{
    ans = 0;
    dfs(0, m, 0);//初始状态
    printf("%lld\n", ans);
}
int main()
{
    for (int i = 0; i < 4; ++i)
    scanf("%d", &c[i]);
    scanf("%d", &t);
    first();
    while(t--)
    {
        init();
        deal();
    }
    return 0;
}


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326291817&siteId=291194637