UVa 818 Cutting Chains 切断圆环链 二进制枚举 判环

题目链接: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;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值