【二进制枚举】 P1441 砝码称重

161 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情

砝码称重

题目描述

现有 nn 个砝码,重量分别为 aia_i,在去掉 mm 个砝码后,问最多能称量出多少不同的重量(不包括 00)。

请注意,砝码只能放在其中一边。

输入格式

11 行为有两个整数 nnmm,用空格分隔。

22 行有 nn 个正整数 a1,a2,a3,,ana_1, a_2, a_3,\ldots , a_n,表示每个砝码的重量。

输出格式

仅包括 11 个整数,为最多能称量出的重量数量。

样例 #1

样例输入 #1

3 1
1 2 2

样例输出 #1

3

提示

【样例说明】

在去掉一个重量为 22 的砝码后,能称量出 1,2,31, 2, 333 种重量。

【数据规模】

对于 20%20\% 的数据,m=0m=0

对于 50%50\% 的数据,m1m\leq 1

对于 50%50\% 的数据,n10n\leq 10

对于 100%100\% 的数据,n20n\leq 20m4m\leq 4m<nm < nai100a_i\leq 100

解题思路:二进制枚举

首先用一个02n10到2^{n-1}的循环枚举状态,然后用popcount判断是否满足条件。

int table[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
int popcount(unsigned int x) { // 返回x的二进制中1的个数
    int ret = 0;
    for(int i = 0; i < 8; i++) ret += table[x & 15], x >>= 4;
    return ret;
}
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
int n,m;
int w[30];
int ans = -inf;
int count_one(int x)//x的二进制中第0到n-1位中1的个数
{
    int cnt = 0;
    for(int i = 0; i <= n - 1; ++i)
        if(x & (1 << i))//注意做完按位与后的结果不是1,如可能为000010000,所以不要写成x & (1 << i) == 1
            cnt++;
    return cnt;
}
int main()
{
    cin >> n >> m;
    //我们在用二进制写题时,下标最好以0开始
    for(int i = 0; i <= n - 1; ++i)
        cin >> w[i];
    //(1 << n) - 1的二进制有n个1	
    for(int i = 0; i <= (1 << n) - 1; ++i)
    {
    	//如果此时的i的二进制中有n-m个1
        if(count_one(i) == n - m)
        {
            bitset<2010> b;
            b[0] = 1;//含义为重量0可以被称出
            for(int j = 0; j <= n - 1; ++j)
                if(i & (1 << j))//如果i的第j位上是1
                    b = b | b << w[j];
            ans = max(ans,(int)b.count());
        }
    }
    cout << ans - 1 << endl;//把重量为0的情况排除
    return 0;
}