P1450 [HAOI2008] 硬币购物 dp 容斥 —— s - c[i]*(d[i]+1)怎么理解

[HAOI2008] 硬币购物 - 洛谷

看了洛谷许多题解,一开始理解不了为什么是 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;
}
相关推荐
郝学胜-神的一滴2 分钟前
Linux信号四要素详解:从理论到实践
linux·服务器·开发语言·网络·c++·程序人生
yangpipi-2 分钟前
《C++并发编程实战》 第3章 在线程间共享数据
开发语言·c++
fish_xk4 分钟前
c++基础
开发语言·c++
MoonBit月兔4 分钟前
审美积累 | MoonBit LOGO 投稿作品速递
开发语言·编程·moonbit
缘三水35 分钟前
【C语言】12.指针(2)
c语言·开发语言·指针
Python学习导航37 分钟前
Python开源项目月排行 2025年10月
开发语言·python
爱吃巧克力的程序媛39 分钟前
Qt 异步编程---概述
开发语言·qt
feifeigo1231 小时前
MATLAB实现两组点云ICP配准
开发语言·算法·matlab
Yang-Never1 小时前
Open GL ES->以指定点为中心缩放图片纹理的完整图解
android·java·开发语言·kotlin·android studio
fengfuyao9851 小时前
粒子群算法(PSO)求解标准VRP问题的MATLAB实现
开发语言·算法·matlab