【2019牛客暑期多校训练营(第二场) - D】Kth Minimum Clique(bfs,tricks)

题干:

链接:https://ac.nowcoder.com/acm/contest/882/D
来源:牛客网
 

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 wiw_iwi​ representing the weight of each vertex.
Following N lines each contains N characters eije_{ij}eij​. There's an edge between vertex i and vertex j if and only if eij="1"e_{ij} =\texttt{"1"}eij​="1".

1≤N≤1001 \leq N \leq 1001≤N≤100
1≤K≤1061 \leq K \leq 10^61≤K≤106
0≤wi≤1090 \leq w_i \leq 10^90≤wi​≤109
eij∈"01"e_{ij} \in \texttt{"01"}eij​∈"01"
eii="0"e_{ii} = \texttt{"0"}eii​="0"
eij=ejie_{ij} = e_{ji}eij​=eji​

输出描述:

Output one line containing an integer representing the answer. If there's less than K cliques, output "-1"\texttt{"-1"}"-1".

示例1

输入

复制

2 3
1 2
01
10

输出

复制

2

说明

An empty set is considered as a clique.

题目大意:

定义一个团的权值就是团中点的权值和。给定一个n个点的图(n<=100),求第K大的团。(K<=1e6)

解题报告:

其实是考虑到:因为是个团,而不是连通图,所以会有一些性质。

其实看到这题,第一反应肯定是2^100枚举,这样肯定是没错的。考虑时间耗费在哪里了呢?因为有一些根本不是团的情况也被你搜索到了。所以考虑优化:直接在团的基础上进行搜索枚举,但是还有个问题,那就是他万一是个完全图,那还是2^100次方呀。

但是还好,这题告诉你了K<=1e6,所以肯定要在这上面做点文章。还是按照刚刚的想法的话肯定是搜索所有的团,然后排个序输出第k个值,但是如果是完全图的话,那团的大小就有2^100个,肯定不能搜索完所有的团,那么考虑,K既然是已知的了,可不可以只搜索到前K大?所以思路就是优先队列的bfs,直接搜到K个就停止,这样总复杂度就是nlogn了。

注意这题有个小坑,那就是不bfs的过程中,加入新节点的时候需要去重,不然肯定能WA啊、、为了处理这个事情有好多种办法,可以直接HASH,也可以用代码中的方法:记录最后个有效节点是哪个节点,下次直接在这个往后面搜索。原理是因为你bfs每一层的时候都保证了只加入一个节点,所以你在此时前面的点都判断过了,所以不需要再加入了。但是你如果不这样判断一波的话,肯定就会有重复的团被算了多次。

贴一个题解:

我们发现我们很难直接高效的算出一张图的第k小团的权值。因此,我们考虑将这个问题转化一下。我们发现,因为权值都是正数,因此如果在一个已知的团上能够再增加一个新结点形成团,那么新的团的权值必定增加。因此,我们如果从空集不断往上去增加新的结点构造成团,那么在这个过程中,权值一定是不断增加的。因此我们只需要从小到大拓展团,并将当前的团的权值丢进一个优先队列里进行维护,最后不断获取到第k小的权值即可。至此,我们需要考虑如何能够高效的在一个团上增加新的结点。我们还可以考虑每次只加当前点后面的点,避免重复。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<bitset>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define FF first
#define SS second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int MAX = 100 + 5;
struct Node {
	bitset<105> bs;
	ll w;
	int last;
	Node(){}
	Node(bitset<105> bs,ll w,int last):bs(bs),w(w),last(last){}
	bool operator<(const Node b)const {
		return w > b.w;
	}
}; 
bitset<105> a[MAX];
int n,k,val[MAX];
ll bfs() {
	priority_queue<Node> pq;
	bitset<105> bb(0);
	k--;
	pq.push(Node(bb,0,0));
	while(pq.size()) {
		Node cur = pq.top();pq.pop();
		if(k == 0) return cur.w;
		k--;
		for(int i = cur.last+1; i<=n; i++) {
			if(cur.bs[i])continue;//==1????
			if((cur.bs & a[i]) == cur.bs) {
				cur.bs[i] = 1;
				pq.push(Node(cur.bs,cur.w + val[i],i));
				cur.bs[i] = 0;
			}
		}	
	}
    return -1;
}
int main()
{
	cin>>n>>k;
	for(int i = 1; i<=n; i++) cin>>val[i];
	for(int x,i = 1; i<=n; i++) {
		for(int j = 1; j<=n; j++) {
			scanf("%1d",&x);
			if(x == 1) a[i][j]=1;
		}
	}
	ll ans = bfs();
	printf("%lld\n",ans);
	return 0 ;
}

错误代码:

错误原因就是因为刚开始忘记去重了。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<bitset>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define FF first
#define SS second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int MAX = 100 + 5;
struct Node {
    bitset<105> bs;
    ll w;
    Node(){}
    Node(bitset<105> bs,ll w):bs(bs),w(w){}
    bool operator<(const Node b)const {
        return w > b.w;
    }
};
bitset<105> a[MAX];
int n,k,val[MAX];
ll bfs() {
    priority_queue<Node> pq;
    bitset<105> bb(0);
    k--;
    pq.push(Node(bb,0));
    while(pq.size()) {
        Node cur = pq.top();pq.pop();
        if(k == 0) return cur.w;
        k--;
        for(int i = 1; i<=n; i++) {
            if(cur.bs[i]) continue;//==1????
            if((cur.bs & a[i]) == cur.bs) {
                cur.bs[i] = 1;
                pq.push(Node(cur.bs,cur.w + val[i]));
                cur.bs[i] = 0;
            }
        }
         
    }
    return -1;
}
int main()
{
    cin>>n>>k;
    for(int i = 1; i<=n; i++) cin>>val[i];
    for(int x,i = 1; i<=n; i++) {
        for(int j = 1; j<=n; j++) {
            scanf("%1d",&x);
            if(x == 1) a[i][j]=1;
        }
    }
    ll ans = bfs();
    printf("%lld\n",ans);
    return 0 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值