锁(NC14732)状压dp

链接

Click here to see the original question

题目描述

106号房间共有n名居民, 他们每人有一个重要度。房间的门上可以装若干把锁。假设共有k把锁,命名为1到k。每把锁有一种对应的钥匙,也用1到k表示。钥匙可以复制并发给任意多个居民。每个106房间的居民持有若干钥匙,也就是1到k的一个子集。如果几名居民的钥匙的并集是1到k,即他们拥有全部锁的对应钥匙,他们都在场时就能打开房门。新的陆战协定规定,一组居民都在场时能打开房门当且仅当他们的重要度加起来至少为m。问至少需要给106号房间装多少把锁。即,求最小的k,使得可以适当地给居民们每人若干钥匙(即一个1到k的子集),使得任意重要度之和小于m的居民集合持有的钥匙的并集不是1到k,而任意重要度之和大于等于m的居民集合持有的钥匙的并集是1到k。

输入

第一行两个整数n和m,0<n<21,0<m<1000000001。
第二行n个整数表示居民们的重要度。
重要度在[1,1000000000]之间。

输出

一个整数表示最少需要多少把锁。

代码

#include <bits/stdc++.h>
#define FIO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;
typedef long long int ll;
ll n, m, impo[30];
int main() {
    FIO;
    cin>>n>>m;
    for(int i = 1; i <= n; i++){
        cin>>impo[i];
    }
    ll ans = 0;//初始的钥匙数为0
    for(int i = 0; i < (1 << n); i++){
        //从0遍历到2^n-1
        //从二进制来看,就是从000...0(n个)遍历到111...1(n个)
        //我们把1视作这个人在场,0视作这个人不在场
        ll cur_sum = 0;//记录在场的人的重要度之和
        ll min_impo = 999999999999;//记录所有不到场的人中重要度的最小值
        for(int j = 0; j < n; j++){
            if((1 << j) & i){
                //1 << j的二进制是只有第j+1位为1,其余都为0,
                // 只有i的2进制第j+1位为1, 这个值才可能为true
                cur_sum += impo[j + 1];
            }else{
                //i的二进制的第j+1位为0
                min_impo = min(min_impo, impo[j + 1]);
            }
        }
        if(cur_sum >= m){
            //已经到场的人的重要性已达标,一定可以打开门,直接跳过
            continue;
        }else{
            //已经到场的人的重要性不达标
            if(cur_sum + min_impo >= m){
                //再来一个人,重要性一定就达标了,就一定可以打开房门
                //就说明未在场的人都拥有至少一把在场的人都没有的钥匙
                //那么就给未在场的人每人一把新钥匙,钥匙数+1
                ans++;
                //而且因为每次遍历,在场的人都不一样,就保证了每次都必须给一把新钥匙才能满足题意
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

如果您觉得我的文章对您有帮助的话,可以点个赞,点个关注,也可以扫描下方二维码关注我。我将在这个公众号上更新自己的学习笔记,以及分享一些算法知识

Study and progress together with me!

img

猜你喜欢

转载自blog.csdn.net/qq_45359344/article/details/107697618