Algorithm Notes

LJXin

记录算法刷题、竞赛思路与编程成长,希望每一篇都讲清楚一点、写扎实一点。

P2036 PERKET

P2036 PERKET

原题链接

解题思路

本题本质是一个 子集枚举问题(位运算)

每种食材有两种状态:选 / 不选
因此一共有 2^n 种选择方案。

我们可以用一个整数 mask 的二进制表示一种选择方案:

  • 第 i 位为 1 → 选第 i 个食材
  • 第 i 位为 0 → 不选

例如(n = 2):

mask 二进制 选择情况
0 00 空集(不合法)
1 01 选第0个
2 10 选第1个
3 11 全选

1. 枚举所有子集

for (int mask = 1; mask < (1 << n); mask++)
  • 1 << n 表示 2^n
  • 从 1 开始是为了排除空集

2. 判断某一位是否被选中

if (mask & (1 << i))

含义:

  • 不为 0 → 选了
  • 为 0 → 没选

3. 计算当前子集的值

  • 酸度:乘积(初始为 1)
  • 苦度:加和(初始为 0)
long long x = 1, y = 0;

4. 更新答案

ans = min(ans, abs(x - y));

复杂度分析

  • 时间复杂度:O(n * 2^n)
  • 空间复杂度:O(n)

代码实现

void solve() {
    int n;
    cin>>n;
    vector<int>s(n),b(n);
    rep(i,0,n) cin>>s[i]>>b[i];
    LL ans=MAX;
    rep(mask,1,1<<n){
        LL x=1,y=0;
        rep(i,0,n){
            if(mask&(1<<i)){
                x*=s[i];
                y+=b[i];
            }
        }
        ans=min(ans,abs(x-y));
    }
    cout<<ans<<'\n';
}


易错点总结

  1. 每个子集必须重新初始化x为1,y为0
  2. 不能包含空集mask=1
  3. 酸度初始必须为1
  4. 复杂度是 O(n * 2^n)

总结

用二进制枚举所有非空子集,每一位表示选或不选,暴力求最优解。

posted @ 2026-03-26 20:03  kliu_365  阅读(19)  评论(0)    收藏  举报
TOP