【洛谷 P8742】[蓝桥杯 2021 省 AB] 砝码称重 题解(动态规划+01背包+位集合)

文章介绍了通过位集数据结构解决天平与N个不同重量砝码称重问题的步骤和AC代码实现。
摘要由CSDN通过智能技术生成

[蓝桥杯 2021 省 AB] 砝码称重

题目描述

你有一架天平和 N N N 个砝码, 这 N N N 个砝码重量依次是 W 1 , W 2 , ⋯   , W N W_{1}, W_{2}, \cdots, W_{N} W1,W2,,WN 。 请你计算一共可以称出多少种不同的重量?

注意砝码可以放在天平两边。

输入格式

输入的第一行包含一个整数 N N N

第二行包含 N N N 个整数: W 1 , W 2 , W 3 , ⋯   , W N W_{1}, W_{2}, W_{3}, \cdots, W_{N} W1,W2,W3,,WN

输出格式

输出一个整数代表答案。

样例 #1

样例输入 #1

3
1 4 6

样例输出 #1

10

提示

【样例说明】

能称出的 10 种重量是: 1 、 2 、 3 、 4 、 5 、 6 、 7 、 9 、 10 、 11 1 、 2 、 3 、 4 、 5 、 6 、 7 、 9 、 10 、 11 123456791011

1 = 1 2 = 6 − 4 (  天平一边放  6 ,  另一边放 4)  3 = 4 − 1 4 = 4 5 = 6 − 1 6 = 6 7 = 1 + 6 9 = 4 + 6 − 1 10 = 4 + 6 11 = 1 + 4 + 6 \begin{aligned} &1=1 \\ &2=6-4(\text { 天平一边放 } 6, \text { 另一边放 4) } \\ &3=4-1 \\ &4=4 \\ &5=6-1 \\ &6=6 \\ &7=1+6 \\ &9=4+6-1 \\ &10=4+6 \\ &11=1+4+6 \end{aligned} 1=12=64( 天平一边放 6, 另一边放 4) 3=414=45=616=67=1+69=4+6110=4+611=1+4+6

【评测用例规模与约定】

对于 50 % 50 \% 50% 的评测用例, 1 ≤ N ≤ 15 1 \leq N \leq 15 1N15

对于所有评测用例, 1 ≤ N ≤ 100 , N 1 \leq N \leq 100, N 1N100,N 个砝码总重不超过 1 0 5 10^5 105

蓝桥杯 2021 第一轮省赛 A 组 F 题(B 组 G 题)。


思路

首先,定义一些常量和变量。其中,N 是砝码的最大数量。变量 n 是砝码的数量,w[N] 是一个数组,用于存储每个砝码的重量,dp 是一个位集,用于存储可以称出的所有重量,每个位对应一个重量,如果该位为 1,表示可以称出该重量,否则表示不能称出该重量。

然后,通过 cin 从输入流中读取砝码的数量 n 和每个砝码的重量 w[i]。

接下来,初始化 dp[0] 为 1,表示没有砝码时,能称出的重量为0。然后,对于每个砝码,都会创建一个临时的位集 dpt,并将其设置为当前的 dp。

遍历所有可能的重量 j,如果当前重量 j 可以被称出(即 dp[j] 为 1),则更新 dpt[j + w[i]] 和 dpt[abs(j - w[i])] 为 1,表示可以称出新的重量 j + w[i] 和 abs(j - w[i])。最后,将 dp 设置为 dpt。

最后,将 dp[0] 设置为 0,表示不能称出重量为0,然后通过调用 dp.count() 来计算可以称出的重量的数量,并将结果输出到输出流。

注意

在计算 dp[j + w[i]]dp[abs(j - w[i])] 的值时,需要使用到未更新前的 dp[j] 的值,如果直接在 dp 上进行更新,那么在计算 dp[j + w[i]]dp[abs(j - w[i])] 的值时使用的 dp[j] 可能已经被更新过,这样就会导致结果错误。所以需要使用 dpt 来暂时存储更新的结果,等所有的 j 都遍历完后,再将 dp 设置为 dpt


AC代码

#include <algorithm>
#include <bitset>
#include <iostream>
#define AUTHOR "HEX9CF"
using namespace std;
using ll = long long;

const int N = 1e6 + 7;
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;

int n;
int w[N];
bitset<N> dp;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> w[i];
	}

	dp[0] = 1;
	for (int i = 1; i <= n; i++) {
		bitset<N> dpt = dp;
		for (int j = 1e5; j >= 0; j--) {
			if (dp[j]) {
				dpt[j + w[i]] = 1;
				dpt[abs(j - w[i])] = 1;
			}
		}
		dp = dpt;
	}

	// for (int i = 1; i <= 1000; i++) {
	// 	if (dp[i]) {
	// 		cout << i << "\n";
	// 	}
	// }
	dp[0] = 0;
	cout << dp.count() << "\n";
	return 0;
}

  • 27
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值