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
", ++kase, solve());
    }
    return 0;
}
原文地址:https://www.cnblogs.com/dwtfukgv/p/5601681.html