AcWing 3417:砝码称重 ← bitset

【题目来源】
https://www.acwing.com/problem/content/3420/

【题目描述】
你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1,W2,⋅⋅⋅,WN。
请你计算一共可以称出多少种不同的正整数重量?
注意砝码可以放在天平两边。

【输入格式】
输入的第一行包含一个整数  N。
第二行包含 N 个整数:W1,W2,W3,⋅⋅⋅,WN。

【输出格式】
输出一个整数代表答案。

【数据范围】
对于 50% 的评测用例,1≤N≤15。
对于所有评测用例,1≤N≤
100,N 个砝码总重不超过 10^5

【输入样例】
3
1 4 6

【输出样例】
10

【算法分析】
● 给定的砝码样例,能称出 10 种重量,分别是:
1、2、3、4、5、6、7、9、10、11。

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。

● C++ 的 STL bitset 的一个重要应用场景是优化动态规划算法‌。bitset的位操作(移位、或运算)比传统循环更高效。相比传统数组或哈希表,位运算节省空间且支持并行操作。
详见:
https://blog.csdn.net/hnjzsyjyj/article/details/148285903

● 在动态规划问题中,s|=s<<w[i] 及 s|=s>>w[i] 是高效的位运算技巧,用于实现状态转移和集合合并。以下介绍其核心原理:
(1)
二进制状态表示‌
‌集合编码‌:用二进制位表示可达状态集合。例如 101001 对应 {0,3,5}
(2)
左移/右移操作的意义‌
加法扩展(s<<w[i])‌:左移相当于对集合中所有元素加 w[i]。例如 101001<<2 生成 10100100,对应 {2,5,7}。即 {2,5,7} 是将 101001 对应的 {0,3,5}  中每个元素加 2 后所得 。
减法扩展(s>>w[i])‌:右移相当于对集合中所有元素减 w[i],需确保结果非负。例如 10100100>>2 生成
00101001,对应 {0,3,5}。即 {0,3,5} 是将 10100100 对应的 {2,5,7}  中每个元素减 2 后所得 。注意:负数去掉。
(3)
或运算的并集功能‌
集合合并(|=)‌:通过按位或运算合并新旧集合。例如 10100100 | 00101001=10101101,对应 {0,2,3,5,7}。即将 10100100 对应的 {2,5,7} 与 00101001 对应的 {0,3,5} 取并集。

【算法代码】

#include <bits/stdc++.h>
using namespace std;

const int maxn=1e2+5;
const int maxm=1e5+5;
int g[maxn];
bitset<maxm> s;
int n;

int main() {
    cin>>n;
    for(int i=0; i<n; i++) cin>>g[i];

    s[0]=1;
    for(int i=0; i<n; i++) s|=s<<g[i];
    for(int i=0; i<n; i++) s|=s>>g[i];
    cout<<s.count()-1<<endl;
    return 0;
}

/*
in:
3
1 4 6

out:
10
*/

【代码解析】
(1)输入处理
读取砝码数量 n 和每个砝码的重量 w[i]。
(2)bitset 初始化

s[0]=1 表示重量 0 是可以称出的初始状态
bitset 的每一位代表一个可能的重量值。
(3)核心计算
第一个循环 s|=s<<w[i] 计算将砝码放在天平一侧能称出的重量。
第二个循环 s|=s>>w[i] 计算将砝码放在天平另一侧能称出的重量。
通过位运算高效地更新所有可能的重量组合。
(4)输出结果

s.count()-1 计算所有能称出的非零重量数量




【参考文献】
https://www.acwing.com/solution/content/45929/
https://www.acwing.com/problem/content/3420/
https://blog.csdn.net/hnjzsyjyj/article/details/148285903
https://www.acwing.com/solution/content/160343/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值