• 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 }
  • 相关阅读:
    c文件操作库
    双链常用操作2
    双向链表常用操作
    c队列操作
    c日期格式化操作之date
    单链常用操作类
    c字符串常用操作
    双向链表通用类
    c栈操作
    poj2509
  • 原文地址:https://www.cnblogs.com/zyb993963526/p/6350198.html
Copyright © 2020-2023  润新知