UVa 818 切断圆环链(dfs+二进制枚举)

Posted 谦谦君子,陌上其华

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 }

 

以上是关于UVa 818 切断圆环链(dfs+二进制枚举)的主要内容,如果未能解决你的问题,请参考以下文章

UVA818-Cutting Chains(二进制枚举+dfs判环)

UVA 818 Cutting Chains

UVa 818Cutting Chains (暴力dfs+位运算+二进制法)

UVa 818 Cutting Chains 题解

UVa 1354 天平难题 (枚举二叉树)

UVA690-Pipeline Scheduling(dfs+二进制压缩状态)