题解 | Kth minimum Clique-2019牛客暑期多校训练营第二场D题

题目来源于牛客竞赛:https://ac.nowcoder.com/acm/contest/discuss
题目描述:
Given a vertex-weighted graph with N vertices, find out the K-th minimum weighted clique.

A subset of vertices of an undirected graph is called clique if and only if every two distinct vertices in the subset are adjacent. The weight of a clique is the summation of the weight of vertices in it.

输入描述:
The first line of input contains two space-separated integers N, K.
The second line of input contains N space-separated integers wi representing the weight of each vertex.
Following N lines each contains N characters eij. There’s an edge between vertex i and vertex j if and only if eij= “1”.

1≤N≤100
1≤K≤106
0≤wi≤109
eij∈“01”
eii=0
eij=eji

输出描述:
Output one line containing an integer representing the answer. If there’s less than K cliques, output “-1”.

示例1:
输入
2 3
1 2
01
10

输出
2

说明
An empty set is considered as a clique.

题解:
• Brute force, bitmask
We just need to brute force!
By maintaining the current clique, potential new vertex to add, and current weight sum, we can find out next possible clique in O(1)!.

• Brute force, bitmask
By maintaining the current clique, potential new vertex to add, and current weight sum, we can find out next possible clique in O(1)!.
By extracting out the lowest bit of potential set(maintaining in bitmask), we can either construct another valid clique(Yeah!) or found that sum of weights exceeds V. Thus, for each clique, we will either find out another clique or stop searching in O(1). We only need to find out at most K cliques for each V. Results in an O(K \lg MAXV) solution.

• Brute force, bitmask
First, let sort all the vertice by their weight.
Then, binary search the answer V.
We need to find out whether there are K or more or less cliques with value <= V.
We just need to brute force!

代码:

// Author: Yen-Jen Wang
#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;

typedef long long ll;

const __int128 ONE = 1;
const int MAX_N = 100 + 7;

int N, K;
int W[MAX_N];
vector<int> odr;
__int128 G[MAX_N];
char S[MAX_N][MAX_N];

void input() {
    scanf("%d%d", &N, &K);
    for (int i = 0; i < N; i++) 
        scanf("%d", &W[i]);
    for (int i = 0; i < N; i++) {
        scanf("%s", S[i]);
        for (int j = 0; j < N; j++)
            S[i][j] -= '0';
    }
}

int popcount(__int128 x) {
    return __builtin_popcountll(x >> 50) + __builtin_popcount(x & ((ONE << 50) - 1));
}

int lowbit(__int128 x) {
    if ((x & ((ONE << 50) - 1)) == 0)
        return __builtin_ctzll((ll)(x >> 50)) + 50;
    else
        return __builtin_ctzll((ll)(x & ((ONE <<50) - 1)));
}

void build() {
    for (int i = 0; i < N; i++)
        odr.push_back(i);
    sort(odr.begin(), odr.end(), [&] (const int x, const int y) {
        return W[x] < W[y];
    });

    static int mapping[MAX_N];
    for (int i = 0; i < N; i++)
        mapping[odr[i]] = i;

    static int W_tmp[MAX_N];
    for (int i = 0; i < N; i++)
        W_tmp[i] = W[i];
    for (int i = 0; i < N; i++) 
        W[mapping[i]] = W_tmp[i];
    
    static char S2[MAX_N][MAX_N];
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) 
            S2[i][j] = S[i][j];
    }
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) 
            S[mapping[i]][mapping[j]] = S2[i][j];
    }
    for (int i = 0; i < N; i++) {
        G[i] = 0;
        for (int j = 0; j < N; j++) {
            if (S[i][j]) {
                G[i] |= (ONE << j);
            }
        }
    }
}

int cnt;
ll limit;

void dfs(__int128 now, __int128 can, ll sum) {
    if (sum <= limit)
        cnt++;
    else
        return;
    
    if (cnt >= K)
        return;

    while (can) {
        int p = lowbit(can);
        can ^= (ONE << p);

        dfs(now | (ONE << p), can & G[p], sum + W[p]);

        //if ((now & G[p]) != now)
            //continue;
        
        if (sum + W[p] > limit)
            break;
        
        //dfs(now | (ONE << p), can, sum + W[p]);
        
        if (cnt >= K)
            break;
    }
}

bool check(ll x) {
    cnt = 0;
    limit = x;
    dfs(0, (ONE << N) - 1, 0);
    return cnt >= K;
}

void solve() {    
    ll lb = 0, rb = 0;
    for (int i = 0; i < N; i++)
        rb += W[i];
    while (lb < rb) {
        ll mid = (lb + rb) >> 1;
        if (check(mid))
            rb = mid;
        else
            lb = mid + 1;
    }
    if (check(lb))
        printf("%lld\n", lb);
    else
        puts("-1");
}

int main() {
    input();
    build();
    solve();
    return 0;
}

更多问题,更详细题解可关注牛客竞赛区,一个刷题、比赛、分享的社区。
传送门:https://ac.nowcoder.com/acm/contest/discuss

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值