zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    从 mian 函数开始一步一步分析 nginx 执行流程(四)
    从 mian 函数开始一步一步分析 nginx 执行流程(三)
    从 mian 函数开始一步一步分析 nginx 执行流程(二)
    从 mian 函数开始一步一步分析 nginx 执行流程(一)
    QQ游戏百万人同时在线服务器架构实现
    HTTP协议详解
    centos7用yum搭建LAMP环境
    msfvenom 木马生成(常用)
    用Digispark制作BadUSB+msf植入病毒木马
    kvm管理工具Webvirtmgr安装
  • 原文地址:https://www.cnblogs.com/dwtfukgv/p/5601681.html
Copyright © 2011-2022 走看看