zoukankan      html  css  js  c++  java
  • UVA 818 Cutting Chains(切断圆环链)(dfs + 二进制法枚举子集)

    题意:有n个圆环(n<=15),已知已经扣在一起的圆环,现在需要打开尽量少的圆环,使所有圆环可以组成一条链。

    分析:因为不知道要打开哪个环,如果列举所有的可能性,即枚举打开环的所有子集,最多才2^15,即32768。

    1、二进制法生成打开环的所有子集

    2、枚举每一种子集,环打开后,此环就是孤立的,剩下的环也不与之相连,若剩下的环满足下列所有条件,则这种子集成立,进而最终比较打开环的最少个数。

    (1)每个环与之相连的环的个数不超过2。

    (2)剩下的环里没有圈,dfs判圈,连通块涂色。

    (3)上述处理后,剩下的环形成了几条链状的连通块,通过打开的环将这些连通块连接。所以需要满足打开环的个数大于等于连通块个数减1。

    #pragma comment(linker, "/STACK:102400000, 102400000")
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<cmath>
    #include<iostream>
    #include<sstream>
    #include<iterator>
    #include<algorithm>
    #include<string>
    #include<vector>
    #include<set>
    #include<map>
    #include<stack>
    #include<deque>
    #include<queue>
    #include<list>
    #define Min(a, b) ((a < b) ? a : b)
    #define Max(a, b) ((a < b) ? b : a)
    typedef long long ll;
    typedef unsigned long long llu;
    const int INT_INF = 0x3f3f3f3f;
    const int INT_M_INF = 0x7f7f7f7f;
    const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
    const ll LL_M_INF = 0x7f7f7f7f7f7f7f7f;
    const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1};
    const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1};
    const int MOD = 1e9 + 7;
    const double pi = acos(-1.0);
    const double eps = 1e-8;
    const int MAXN = 10000 + 10;
    const int MAXT = 10000 + 10;
    using namespace std;
    set<int> s[20];
    int n;
    int vis[20];
    int mark[20];
    bool flag;
    void dfs(int x, int cnt, int fa){//fa是指向当前连通块的来源
        if(flag) return;
        if(mark[x] == cnt){
            flag = true;//如果要染色的连通块已有颜色,则代表有圈
            return;
        }
        mark[x] = cnt;
        for(set<int>::iterator it = s[x].begin(); it != s[x].end(); ++it){
            if(!vis[*it] && *it != fa){
                dfs(*it, cnt, x);
            }
        }
    }
    bool judge(int num){
        //判断剩下的环与几个环连着
        for(int i = 1; i <= n; ++i){
            if(!vis[i]){
                int cnt = 0;
                for(set<int>::iterator it = s[i].begin(); it != s[i].end(); ++it){
                    if(!vis[*it]) ++cnt;
                }
                if(cnt > 2) return false;
            }
        }
        //dfs判断剩下的环里有无圈,连通块涂色
        memset(mark, 0, sizeof mark);
        int cnt = 0;//连通块个数
        flag = false;
        for(int i = 1; i <= n; ++i){
            if(!vis[i] && !mark[i]){
                ++cnt;
                dfs(i, cnt, -1);//cnt连通块涂色编号
            }
            if(flag) return false;
        }
        return num >= cnt - 1;//如果打开环的个数大于等于连通块个数减1才成立
    }
    void solve(int kase){
        int ans = INT_M_INF;
        for(int i = 0; i < (1 << n); ++i){
            memset(vis, 0, sizeof vis);
            int cnt = 0;//打开环的个数
            for(int j = 0; j < n; ++j){
                if(i & (1 << j)){
                    vis[j + 1] = 1;//下标为1~n
                    ++cnt;
                }
            }
            if(judge(cnt)) ans = Min(ans, cnt);
        }
        printf("Set %d: Minimum links to open is %d\n", kase, ans);
    }
    int main(){
        int kase = 0;
        while(scanf("%d", &n) == 1){
            if(!n) return 0;
            for(int i = 0; i < 20; ++i) s[i].clear();
            int x, y;
            while(scanf("%d%d", &x, &y) == 2){
                if(x == -1 && y == -1) break;
                s[x].insert(y);
                s[y].insert(x);
            }
            ++kase;
            solve(kase);
        }
        return 0;
    }
  • 相关阅读:
    洛谷 P3521 [POI2011]ROT-Tree Rotations 解题报告
    洛谷 P1640 [SCOI2010]连续攻击游戏 解题报告
    vector-pop_back
    vector-push_back
    vector-push_back
    vector-max_size
    vector-max_size
    vector-insert
    vector-insert
    vector-front
  • 原文地址:https://www.cnblogs.com/tyty-Somnuspoppy/p/6307687.html
Copyright © 2011-2022 走看看