【2019牛客暑期多校训练营 第二场 D题 Kth Minimum Clique】【优先队列+bitset】

题目链接:
https://ac.nowcoder.com/acm/contest/882/D
题意:
N N N个点,每个点有对应的权 w i w_i wi,还有边 e i j ∈ { 0 , 1 } e_{ij}∈\left\{ 0,1 \right \} eij{0,1} e i j = 1 e_{ij} = 1 eij=1表示 i i i j j j连通,现求第 K K K大的团的值。
题解:
因为权值都为正值,所以在当前团的基础上,你每多加一个点,就会使团的值变大,所以可以考虑优先队列,一个一个的加入点让团的值由小到大。这里需要保证新加入的点必须与当前团中的点是连通的,如果是用vector数组记录的话太费空间(每个点团都要记录),这里用到了bitset,因为我们只需要知道哪些点在团中,所以对应二进制位数如果是1那就是在,只要新加入的点的状态与当前团的状态按位与结果不变,即(now.bt & state[i]) != now.bt,那就能说明新加入的点与原来的点是连通的。
代码:

const int MAX = 110;

struct clique {
	int n;//记录上个新加入的点
	ll w;//记录团的值
	bitset<MAX> bt;//bitset记录连通情况
	clique() {
		n = 0; 
		w = 0;
		bt.reset();
	}
	bool operator < (const clique& a) const {//优先队列比较
		return w > a.w;
	}
};

int N, K;
ll w[MAX];
bitset<MAX> state[MAX];
char str[MAX];

priority_queue<clique> q;

void solve() {
	clique now;
	q.push(now);
	while (!q.empty()) {
		clique now = q.top(); q.pop(); K--;
		if (K == 0) {//说明当前就是第K个
			printf("%lld\n", now.w);
			return;
		}
		for (int i = now.n + 1; i <= N; i++) {//从当前团上一个新加入的点之后开始,避免重复状态入队
			clique nxt = now;
			if ((now.bt & state[i]) != now.bt)//当前团和新加入点按位与结果不变  说明都是连通的
				continue;
			nxt.bt.set(i);// -> 团中有i这个点
			nxt.n = i;
			nxt.w += w[i];
			q.push(nxt);
		}
	}
	printf("-1\n");
}


int main() {
	scanf("%d%d", &N, &K);
	for (int i = 1; i <= N; i++)
		scanf("%lld", &w[i]);
	for (int i = 1; i <= N; i++) {
		scanf("%s", str + 1);
		for (int j = 1; j <= N; j++)
			if (str[j] == '1')
				state[i].set(j);//位运算表示map[i][j]=1
	}
	solve();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值