UVA818 切断圆环链 Cutting Chains

知识点:深搜,剪枝,并查集

这个题还是有点坑的,坑的最长时间的坑点就是题目的输入里面是会有重边的,如果不去消去这些重边,仅仅只是多开点数组以保能储存所有的,那么是会出错的,应该是会在判断两个点是不是联通的地方出错,

然后看这个题可以用指数枚举来做,因为数据很小,才15,2的15次方是3e4,在加上剪枝,应该是能过的,时间复杂度应该是前面再乘以边的数目,我们指数枚举,用散列数组来记录哪个环是开开的,然后到递归的终点的时候,做三个判断,我们一边遍历输入的边,如果有的边的连边的点是打开的,那么这个边是不用连接的,三个判断是,1如果有环,那么不行,2如果有点的入度大于2那么不行,如果连通块的个数大于开开的点的个数的二倍加一,那么不行,这个三个判断一下,就行了,这里应该是属于思维题的类型,用并查集来维护连通性,并且来判环,

然后就是我一开始一直错的地方,一个就是一开始的最优性剪枝写错了,一开始写的是等于,那么剪枝,这里很显然是错的,因为我们更新答案是随机的,不会是刚好每次减少一个这样,这里有点想当然了,然后一个地方就是,这个题的输入里面是会有重边的,这里的重边对这个题的正确答案不会起任何作用,但是会让答案判断的时候出错,所以这个输入的时候是要去掉重边的,

#include <bits/stdc++.h>

using namespace std;

const int N = 20;
const int M = 205;

int n, a[M], b[M], m, fa[N], h[N], cnt, ans;

int get(int x) {
	if (x == fa[x]) return x;
	return fa[x] = get(fa[x]);
}

void merge(int x, int y) {
	fa[get(x)] = get(y);
}

void dfs(int dep) {
	if (cnt >= ans) return;
	if (dep == n + 1) {
		int ok = 1, ind[N] = {};
		for (int i = 1; i <= n; i++) fa[i] = i;
		for (int i = 1; i <= m && ok; i++) {
			if (h[a[i]] || h[b[i]]) continue;
			if (get(a[i]) == get(b[i])) ok = 0;
			else {
				merge(a[i], b[i]);
				if (++ind[a[i]] > 2) ok = 0;
				if (++ind[b[i]] > 2) ok = 0;
			}
		}
		int num = 0, c[N] = {};
		for (int i = 1; i <= n; i++) {
			if (++c[get(i)] == 1) num++;
		}
		if (ok && num <= cnt * 2 + 1) ans = min(ans, cnt);
		return;
	}
	dfs(dep + 1);
	h[dep] = 1;
	cnt++;
	dfs(dep + 1);
	h[dep] = 0;
	cnt--;
}

int main() {
	int T = 1;
	while (scanf("%d", &n) && n) {
		m = 0;
		int x, y;
		set<pair<int, int>> st;
		while (scanf("%d%d", &x, &y) && x != -1) {
			if (x > y) swap(x, y);
			if (st.find(make_pair(x, y)) != st.end()) continue;
			st.insert(make_pair(x, y));
			a[++m] = x; b[m] = y;
		}
		ans = n;
		dfs(1);
		printf("Set %d: Minimum links to open is %d\n", T++, ans);
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值