AcWing 3417:砝码称重——位集合
【题目来源】
3417. 砝码称重 - AcWing题库
【题目描述】
你有一架天平和 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 = 4 + 1;
6 = 6;
7 = 1 + 6;//注意没有8
9 = 4 + 6 − 1;
10 = 4 + 6;
11 = 1 + 4 + 6。
●C++ 的 STL bitset 的位操作(移位、或运算等)比传统显示循环更高效。相比传统数组或哈希表,位运算节省空间且支持并行操作。
● 在砝码称重问题中,通过 bitset 的左右移位操作模拟加减法,替代显示循环,确实巧妙。
(1)左移(<<)的加法效应:左移 w[i] 位等价于数值乘以 2^w[i],但在 bitset 中实际表示将当前所有可称重量 x 增加 w[i]。
物理意义:模拟砝码放在物品异侧(天平另一端),称重结果为 x+w[i]。
示例:若 bitset 状态为 101001(表示 {0,3,5} 克),左移 2 位后状态为 10100100(表示 {2,5,7} 克,即 {0+2,3+2,5+2}。)
(2)右移(>>)的减法效应:右移 w[i] 位等价于数值除以 2^w[i],但在 bitset 中实际表示将当前可称重量 x 减去 w[i] 克(仅保留 x≥w[i] 的结果)。
物理意义:模拟砝码放在物品同侧(天平同一端),称重结果为 |x - w[i]|。
示例:若 bitset 状态为 10100100(表示 {2,5,7} 克),右移 2 位后状态为 101001(表示 {0,3,5} 克,即 {2-2,5-2,7-2}。)//注意,如果是负数则去掉
● 在砝码称重问题中,s|=s<<w[i] 及 s|=s>>w[i] 是高效的位运算技巧,用于实现状态转移和集合合并。以下介绍或运算( | )的并集功能。
集合合并(|=):通过按位或运算合并新旧集合。例如 10100100 | 101001=10101101,对应 {0,2,3,5,7}。即将 10100100 对应的 {2,5,7} 与 101001 对应的 {0,3,5} 取并集。
【Code】
#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;//s[0]=1 表示重量 0 是可以称出的初始状态,
bitset 的每一位代表一个可能的重量值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;
}