题目描述
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 wiwi representing the weight of each vertex.
Following N lines each contains N characters eijeij. There's an edge between vertex i and vertex j if and only if eij="1"eij="1". 1N1001≤N≤100 1K1061≤K≤106 0wi1090≤wi≤109 eij"01"eij∈"01" eii="0"eii="0" eij=ejieij=eji

输出描述:

Output one line containing an integer representing the answer. If there's less than K cliques, output "-1""-1".
示例1

输入

复制
2 3
1 2
01
10

输出

复制
2

说明

An empty set is considered as a clique.

我们发现我们很难直接高效的算出一张图的第k小团的权值。因此,我们考虑将这个问题转化一下。我们发现,因为权值都是正数,因此如果在一个已知的团上能够再增加一个新结点形成团,那么新的团的权值必定增加。因此,我们如果从空集不断往上去增加新的结点构造成团,那么在这个过程中,权值一定是不断增加的。因此我们只需要从小到大拓展团,并将当前的团的权值丢进一个优先队列里进行维护,最后不断获取到第k小的权值即可。至此,我们需要考虑如何能够高效的在一个团上增加新的结点。
我们还可以考虑每次只加当前点后面的点,避免重复,下面贴一个不用bitset写的代码,如果超时可能是网络波动问题,再交一发就行了。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3fffffff
#define maxn 100005
#define mod 1000000000
int n,m,k,t;
ll u[105];
struct ac{
    ll wight;
    int z;
    vector<int>dis;
    bool operator<(const ac & x)const{return wight>x.wight;}
}a[105],f;
bool ma[105][105];
priority_queue<ac>q;
int main(){
    scanf("%d%d",&n,&k);
    k--;//最小为一个点都没有的团,要删去
    for(int i=1;i<=n;i++)scanf("%d",&u[i]);
    getchar();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            char c=getchar();
            if(c=='1')
                ma[i][j]=1;
        }
        getchar();
    }
    if(k==0){
        puts("0");
        return 0;
    }
    for(int i=1;i<=n;i++){//先把每个点都放入优先队列
        a[i].wight=u[i];
        a[i].z=0;
        a[i].dis.push_back(i);
        q.push(a[i]);
    }
    while(!q.empty()){
        f=q.top();//取出当前最小的团
        k--;
        q.pop();
        if(k==0){
            printf("%lld\n",f.wight);
            return 0;
        }
        int now=f.dis[f.z]+1;//取当前点后面的点,去重
        for(int i=now;i<=n;i++){
            bool flag=1;
            for(int j=0;j<=f.z;j++){//如果新的点与之前所有点相连,加入这个团
                if(ma[f.dis[j]][i]==0){
                    flag=0;
                    break;
                }
            }
            if(flag){
                f.wight+=u[i];
                f.z++;
                f.dis.push_back(i);
                q.push(f);
                f.z--;
                f.dis.pop_back();
                f.wight-=u[i];
            }
        }
    }
    puts("-1");
}