题目链接:Cutting Chains
题目描述:
给定 n ( ≤ 15 ) n(\le15) n(≤15)个圆环,以及一些连接关系表示某些圆环连接在一起,你可以选择一些圆环将这些圆环打开,然后重新连接,问至少要打开多少个圆环才能让所有圆环连接成一条链。
如果有三个圆环三个圆环的连接情况是 1 − > 2 − > 3 1->2->3 1−>2−>3,如果打开 2 2 2那么就会变成 1 , 2 , 3 1, 2, 3 1,2,3三个单独的圆环。
例如给定 6 6 6个圆环连接情况为: 1 − > 2 − > 3 − > 1 , 4 − > 5 − > 6 − > 4 1->2->3->1, 4->5->6->4 1−>2−>3−>1,4−>5−>6−>4,那么可以打开 1 , 4 1, 4 1,4变成: 1 , 2 − > 3 , 4 , 5 − > 6 1, 2->3, 4, 5->6 1,2−>3,4,5−>6,注意此时 1 , 4 1, 4 1,4处于打开状态,那么我们可以将上述四个链直接通过 1 , 4 1, 4 1,4连接成: 2 − > 3 − > 1 − > 4 − > 5 − > 6 2->3->1->4->5->6 2−>3−>1−>4−>5−>6。
题解:
因为最多只有 15 15 15个圆环,我们可以用一个 15 15 15位的二进制来枚举哪些位置的圆环需要打开(用 1 1 1来表示打开)。对于枚举的状态我们只需要判断该状态是否能够让这些圆环练成一条链。
如何判断呢?首先需要知道的是不能有一个圆环有多余两个的圆环与其相连,因为链的话,每个圆环最多和另外两个圆环相连,也就是我们如果将圆环看成结点的话,连接关系看成结点之间的边的话,那么我们需要保证每个结点的度数小于等于 2 2 2。其次由于需要是链,所以在整个图中不能出现环,所以我们还需要判断是否出现环(判断环可以通过 D F S DFS DFS来实现)。但是如果只满足上述两点,并不能保证一定就是一条链了,例如: 1 − > 2 − > 3 1->2->3 1−>2−>3和 4 − > 5 4->5 4−>5虽然满足上述两个条件,但是我们如果想让他们变成一条链的话,那么还需要至少断掉一个圆环才能将两条链进行链接,也就是我们断掉的结点个数要大于等于剩余链的个数减一才能保证拼接起来。
代码:
#include <bits/stdc++.h>
const int MAXN = 15;
using namespace std;
int ans, caseID, n, connectBlockNum, a, b;
bool connected[MAXN][MAXN], vis[MAXN];
// 返回一个数字二进制中有几个1
int getOneBitNumber(int x) {
int cnt = 0;
while (x) {
if (x & 1) { cnt++; }
x >>= 1;
}
return cnt;
}
bool haveCircle(int now, int last, int s) {
if (vis[now]) { return true; }
vis[now] = true;
for (int v = 0; v < n; v++) {
if (s & (1 << v) || v == last || !connected[now][v]) { continue; }
if (haveCircle(v, now, s)) { return true; }
}
return false;
}
void check(int s)
{
for (int i = 0; i < n; i++) {
if (s & (1 << i)) { continue; }
int degree = 0;
for (int j = 0; j < n; j++) {
if (s & (1 << j)) { continue; }
if (connected[i][j]) { degree++; }
}
if (degree > 2) { return; }
}
connectBlockNum = 0;
memset(vis, 0, sizeof(vis));
for (int i = 0; i < n; i++) {
if (s & (1 << i) || vis[i]) { continue; }
connectBlockNum++;
if (haveCircle(i, -1, s)) { return; }
}
if (getOneBitNumber(s) >= connectBlockNum - 1) { ans = min(ans, getOneBitNumber(s)); }
}
int main()
{
ios::sync_with_stdio(false);
while (cin >> n && n != 0) {
memset(connected, 0, sizeof(connected));
ans = n;
while (cin >> a >> b) {
if (a == -1 && b == -1) { break; }
connected[a - 1][b - 1] = connected[b - 1][a - 1] = true;
}
for (int s = 0; s <= (1 << n) - 1; s++) { check(s); }
caseID++;
cout << "Set " << caseID << ": Minimum links to open is " << ans << endl;
}
return 0;
}