知识点:深搜,剪枝,并查集
这个题还是有点坑的,坑的最长时间的坑点就是题目的输入里面是会有重边的,如果不去消去这些重边,仅仅只是多开点数组以保能储存所有的,那么是会出错的,应该是会在判断两个点是不是联通的地方出错,
然后看这个题可以用指数枚举来做,因为数据很小,才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;
}