蓝桥杯2021年第十二届真题第一场-砝码称重

这篇博客探讨了一种使用动态规划解决砝码称重问题的方法。通过定义状态dp[i][j]表示前i个砝码能否称出重量为j的物品,博主详细阐述了状态转移方程的建立过程,包括砝码放在左边和右边的不同情况,并通过代码展示了如何避免负数索引的问题。最终,通过统计能称出不同重量的物品数量,得到了答案。
摘要由CSDN通过智能技术生成

题目

题目链接

题解

动态规划。


状态定义:dp[i][j]表示前i个砝码是否能称出重量为j的物品。

状态转移:对于第i个砝码,选和不选两种情况;对于选又可以分为放在左边和放在右边。

看样例,存在加和减的情况,也就是放在左边和右边的情况。我们规定放在左边用加表示,放在右边用减表示。

那么第i个砝码的全部情况为第i个砝码不放、放左边或放右边。对于重量为j的物品,如果不放第i个砝码,则dp[i][j] |= dp[i-1][j],即若用i-1个砝码可以称出j,那么用i个砝码肯定也行;如果将第i个砝码放在左边(正),那么用i-1个砝码只需要能称出j-w[i]的物品即可,所以dp[i][j] |= dp[i-1][j-w[i]];类似地,将第i个砝码放在右边(负),那么dp[i][j] |= dp[i-1][j+w[i]]


存在一个问题,如此定义转移方程会存在全部砝码全放在左侧或全放在右侧的情况,也就是说会出现负数作为索引的情况。

为了避免这种情况,我们让索引加上一个大数来保证索引为正值。


最后统计能称出不同重量的物品数量时,只需要让j1变化到最大值即可,因为k表示左边-右边=k,即右边与左边之差为k-k表示右边-左边=k,即左边与右边之差为k,本质上都是表示能称出重量为k的物品的意思。所以如果左侧的也统计则相当于统计了两遍。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const int Bais = N;

int n, a[N], ans, m;
bool f[110][N << 1]; // f[i][j]表示前i个砝码是否能称出重量为j的物品 

int main()
{
	cin >> n;
	for (int i = 1;i <= n;i ++) cin >> a[i], m += a[i];
	
	f[0][Bais] = true; // 用0个砝码称出重量为0的物品 
	for (int i = 1;i <= n;i ++)
		for (int j = -m;j <= m;j ++) {
			f[i][j+Bais] |= f[i-1][j+Bais];
			if (j - a[i] >= -m) f[i][j+Bais] |= f[i-1][j-a[i]+Bais];
			if (j + a[i] <= m) f[i][j+Bais] |= f[i-1][j+a[i]+Bais];
		}	

	for (int j = 1;j <= m;j ++) ans += f[n][j+Bais];
	
	cout << ans << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不牌不改

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值