看了洛谷许多题解,一开始理解不了为什么是 s - c[i]*(d[i]+1),为什么要+1呢?
其实是dp理解的不好。
这里的意思就是该枚硬币先超过限制,接下来剩下的背包也要填满,4种硬币随便组合的情况数。
------------------
首先这些题解是假设4种硬币都无限的完全背包。
(dp[ i ] ,i指的钱数,dp值是情况数。)
然后我们思考,这四种硬币是组合的,也就是第一种的所有情况上加第二种的所有情况...
也就是dp[s]已经是四种硬币的组合的情况数了。
容斥定理会吧?(其实证明我也不会,这个大学离散数学里有,但是证明比较奇怪,我没有看懂)
因为四种硬币的数量并不是无限,所以我们上面的dp要减去不合理的。
1,2,3,4可能单独不合理,也可能若干个同时不合理,所以要用容斥定理来计算。(避免重复)
即减去1,2,3,4不合理的,加上12,13,14,23,24,34两个不合理的,减去3个不合理的,加上4个不合理的。
然后就是不合理的数目的计算了,是s - (d[i] + 1) * c[ i ]
是的,我们正常只能对第i个组合到价值d[i] * c[i],但是这里是dp,接下来仍可能接着有c[i]。
所以我们计算不合理的,从不合理的开始 开始计算。
也就是我们取d[ i ]+ 1开始,在dp中接下来会取该硬币若干或者不取,所以从这里开始,这个算1种,接下来仍要填满背包,所以剩下的硬币随便组合,我们算的就是剩下硬币的组合数 ------ dp[ s - (d[i] + 1) * c[ i ] ]。
代码:
cpp
int c[5];
int d[5];
int s;
int getw(int aim)
{
return c[aim] * (d[aim]+1);
}
void solve()
{
for (int i = 1; i <= 4; i++)
{
cin >> c[i];
}
int n;
cin >> n;
vector<int>dp(100005);
dp[0] = 1;
for (int i = 1; i <= 4; i++)
{
for (int j = c[i]; j <= 100000; j++)
{
dp[j] += dp[j - c[i]];
}
}
for (int i = 0; i < n; i++)
{
for (int j = 1; j <= 4; j++)
{
cin >> d[j];
}
cin >> s;
int ans = dp[s];
for (int i = 1; i < 16; i++)
{
int tmp = 0;
int cnt = 0;//奇数减,偶数加,容斥
if (i & 1)
{
tmp += getw(1); cnt++;
}
if (i & 2)
{
tmp += getw(2); cnt++;
}
if (i & 4)
{
tmp += getw(3); cnt++;
}
if (i & 8)
{
tmp += getw(4); cnt++;
}
if (tmp > s)continue;
if (cnt % 2 == 0)
ans += dp[s - tmp];
else
ans -= dp[s - tmp];
}
cout << ans << endl;
}
}
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}