UVa 818Cutting Chains (暴力dfs+位运算+二进制法)
Posted dwtfukgv
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UVa 818Cutting Chains (暴力dfs+位运算+二进制法)相关的知识,希望对你有一定的参考价值。
题意:有 n 个圆环,其中有一些已经扣在一起了,现在要打开尽量少的环,使所有的环可以组成一条链。
析:刚开始看的时候,确实是不会啊。。。。现在有点思路,但是还是差一点,方法也不够好,最后还是参考了网上的题解,大神们的代码就是不一样,
但还是看了好久才看懂。首先是用二进制法进行暴力,因为 n 最大才是15,不会超时的,然后就是在暴力时判断打开这些环时,剩下的是不是还存在环,
如果存在那么不是不行的,然后再判断是不是有的环有两个分支以上,因为一个环如果成链那么最多只有两个分支,所以多于两个的也是不对的,最后,
还要判断打开的环是不是够用,什么意思?你想啊,如果打开的环只有2个,还需要连接的片段环有3个,那么正好,可以用这两个环把它们串起来,
但是片段环多于3个了,那么就不够了,总有几个是连不上的,所以还要满足这个条件,打开的环数要大于等于需要连接的片段减1.剩下的就是怎么判断
成环和分支大于两个了,先说怎么判断怎么计算分支大于两个,在暴力的时候,可以单独计算除了要打开的环并且和它连接的环的数量,如果多于2个,
那么就是分支大于2,再说怎么判断成环,很明显用dfs,从一个环出发,看看能不能再搜回来,如果能那么就是有环,最后是计算,打开了多少环,
这个最简单的是再单独暴力一下,找到一个就加1,最后算出来,当然可以用递归+位运算,就是一个数的二进制中1的数量,可以用按位与进行计算。
但我的代码效率不高,210ms,有点慢。。。
代码如下:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 15 + 5; int n, num; int G[maxn][maxn], vis[maxn]; bool branch(int s){//查找分支是多少 for(int i = 0; i < n; ++i){ if(s & (1<<i)) continue;//要打开的环 int cnt = 0; for(int j = 0; j < n; ++j){ if(s & (1<<j)) continue;//这是要打开的环,不能计算 if(G[i][j]) ++cnt;//如果其他环与其相连接,就加1 } if(cnt > 2) return true;//如果大于2,直接结束 } return false; } bool dfs(int s, int now, int fa){//now 表示当前的环是哪个,fa表示上一环是哪个,因为在搜索的时候不能搜自己 vis[now] = 1; for(int i = 0; i < n; ++i){ if((s & (1<<i)) || !G[now][i] || i == fa) continue;//如果是打开的环或没有连接或者是自己,就跳过 if(vis[i] || dfs(s, i, now)) return true;//如果曾经访问过,也就是又找回来了。或者有环,直接返回 } return false; } bool circle(int s){//判断是不是有环 for(int i = 0; i < n; ++i){ if((s & (1<<i)) || vis[i]) continue;//是打开的环或者是已经访问的环 ++num; if(dfs(s, i, -1)) return true;//如果有环直接返回 } return false; } int cal(int s){ return s == 0 ? 0 : cal(s/2) + (s&1); }//计算要打开的环的数量 int solve(){ int ans = n-1;//最多就是打开n-1个 for(int i = 0; i < (1 << n); ++i){ memset(vis, 0, sizeof(vis)); num = 0; if(branch(i) || circle(i)) continue;//如果有环或者是分支大于2个 if(cal(i) >= num-1) ans = min(ans, cal(i));//如果能够连起来,就更新ans } return ans; } int main(){ // freopen("in.txt", "r", stdin); int kase = 0; while(scanf("%d", &n) == 1 && n){ int u, v; memset(G, 0, sizeof(G)); while(scanf("%d %d", &u, &v) == 2 && u+v > 0){ G[u-1][v-1] = 1; G[v-1][u-1] = 1; } printf("Set %d: Minimum links to open is %d\n", ++kase, solve()); } return 0; }
以上是关于UVa 818Cutting Chains (暴力dfs+位运算+二进制法)的主要内容,如果未能解决你的问题,请参考以下文章