zoukankan      html  css  js  c++  java
  • UVa 818 切断圆环链(dfs+二进制枚举)

    https://vjudge.net/problem/UVA-818

    题意:有n个圆环,其中有一些已经扣在了一起。现在需要打开尽量少的圆环,使得所有圆环可以组成一条链,例如,有5个圆环,1-2,2-3,4-5,则需要打开一个圆环,如圆环4,然   后用它穿过圆环3和圆环5后再次闭合4,就可以形成一条链:1-2-3-4-5。

    思路:从n个圆环中任意选择圆环,这就是枚举子集。所以这道题目可以用二进制枚举来做。

            那么如何判断当前打开圆环是可行的呢?在去除打开的圆环后需要判断:

            ①:每个圆环的分支数都必须小于等于2,大于2个肯定就不能成为单链了。

            ②:不能存在环。

            ③:连通分支数-1不能大于打开圆环数。

            判断是否存在圆环和连通分支数都可以用dfs来实现。

     1 #include<iostream>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 
     6 int n,cnt,number;
     7 int map[20][20];
     8 int vis[20];
     9 int ans;
    10 
    11 bool two(int s)  //判断是否有分支数大于2的圆环
    12 {
    13     for (int i = 0; i < n; i++)
    14     {
    15         int cnt = 0;  //记录分支数
    16         for (int j = 0; j < n; j++)
    17         {
    18             //如果圆环i和j连通并且没有打开i或j时,i圆环的分支数+1
    19             if (map[i][j] && !(s&(1 << i)) && !(s & 1 << j))
    20             {
    21                 cnt++;
    22                 if (cnt == 3)  return true;
    23             }
    24         }
    25     }
    26     return false;
    27 }
    28 
    29 bool dfs(int x,int f,int s)   //判断是否有回路存在
    30 {
    31     vis[x]=1;
    32     for (int i = 0; i < n; i++)
    33     {
    34         if (map[x][i])
    35         {
    36             if (i == f || (s&(1 << i))) continue;  //如果i是上一次访问的圆环或者i圆环被打开,进行下一次判定
    37             if (vis[i])     return true;  //存在回路
    38             if (dfs(i, x, s)) return true;
    39         }
    40     }
    41     return false;
    42 }
    43 
    44 bool circle(int s)
    45 {
    46     memset(vis, 0, sizeof(vis));
    47     for (int i = 0; i < n; i++)
    48     {
    49         if (!vis[i] && !(s & (1 << i)))
    50         {
    51             number++;   //连通分量数+1
    52             if (dfs(i , -1, s)) return true;
    53         }
    54     }
    55     return false;
    56 }
    57 
    58 int calc(int s)  //计算出打开圆环的个数
    59 {
    60     int cnt = 0;
    61     for (int j = 0; j < n; j++)
    62     {
    63         if (s&(1 << j))   cnt++;
    64     }
    65     return cnt;
    66 }
    67 
    68 void solve()
    69 {
    70     ans = 100000;
    71     for (int i = 0; i < (1 << n); i++)  //二进制枚举打开圆环的情况
    72     {
    73         number = 0;
    74         if (two(i) || circle(i))  continue;  //如果不行,进行下一次判断,如果不存在两个分支或回路,则正好计算出了连通分支数
    75         int count = calc(i);
    76         if (number - 1 <= count)  ans = min(ans, count);  //连通分支数-1不能多于打开的圆环数
    77     }
    78 }
    79 
    80 int main()
    81 {
    82     //freopen("D:\txt.txt", "r", stdin);
    83     int a, b, kase=0;
    84     while (cin >> n && n)
    85     {
    86         memset(map, 0, sizeof(map));
    87         while (cin >> a >> b && a != -1 && b != -1)
    88         {
    89             map[a-1][b-1] = 1;
    90             map[b-1][a-1] = 1;
    91         }
    92         solve();
    93         cout<<"Set "<<++kase<<": Minimum links to open is "<<ans<<endl;
    94     }
    95     return 0;
    96 }
  • 相关阅读:
    Max History CodeForces
    Buy a Ticket CodeForces
    AC日记——字符串的展开 openjudge 1.7 35
    AC日记——回文子串 openjudge 1.7 34
    AC日记——判断字符串是否为回文 openjudge 1.7 33
    AC日记——行程长度编码 openjudge 1.7 32
    AC日记——字符串P型编码 openjudge 1.7 31
    AC日记——字符环 openjudge 1.7 30
    AC日记——ISBN号码 openjudge 1.7 29
    AC日记——单词倒排 1.7 28
  • 原文地址:https://www.cnblogs.com/zyb993963526/p/6350198.html
Copyright © 2011-2022 走看看