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;
}
时间: 2024-10-14 04:26:09

UVa 818Cutting Chains (暴力dfs+位运算+二进制法)的相关文章

UVA11127- Triple-Free Binary Strings(DFS+位运算)

题目链接 题意:给出长度为n的字符串,字符串由'1','0','*'组成,其中'*'可以任意替换为'1','0',求不存在连续3个相同子串的字符串的最多个数. 思路:我们可以利用二进制的形式来表示字符串,进行DFS.利用位运算的左移来表示在'*'位置上放置'1',注意在递归的过程中注意判断之否存在3个连续相同的子串. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <alg

皇后问题(DFS)(位运算)

皇后问题 题目描述: 有一个n*n的棋盘,你要在这个棋盘上放上n个皇后,使得她们不能相互攻击.当然为了使得问题更加有趣,我们在棋盘上限定了一些位置使得这些位置上不能放皇后. 输入格式: 第一行两个数n和m ,表示在一个n*n的棋盘上放n个皇后,有m个受限位置. 接下来m行每行两个数,x和y,表示第x行,第y列这个位置不可以放皇后. 输出格式: 一行一个数 ans,表示总的方案数. 样例输入: 4 1 1 2 样例输出: 1 朴素算法(60分) #include<iostream> using

UVa 1151 Buy or Build (最小生成树+二进制法暴力求解)

题意:给定n个点,你的任务是让它们都连通.你可以新建一些边,费用等于两点距离的平方(当然越小越好),另外还有几种“套餐”,可以购买,你购买的话,那么有些边就可以连接起来, 每个“套餐”,也是要花费的,让你求出最少花费. 析:首先想到的是把所有情况都考虑算一下,然后找出最少的,先算没有“套餐”的,然后算有的,用二进制枚举的话,总时间复杂度为O(2qn2+n2logn),这个时间复杂度太大了吧,肯定会超时, 那么我们就可以优化一下,首先先算出来最小生成树,并且把每条边都保存下来,那么加了“套餐”之后

uva 10718 Bit Mask (位运算)

uva 10718 Bit Mask In bit-wise expression, mask is a common term. You can get a certain bit-pattern using mask. For example, if you want to make first 4 bits of a 32-bit number zero, you can use 0xFFFFFFF0 as mask and perform a bit-wise AND operation

Java中的二进制及基本的位运算

Java中的二进制及基本的位运算 二进制是计算技术中广泛采用的一种数制.二进制数据是用0和1两个数码来表示的数.它的基数为2,进位规则是"逢二进一",借位规则是"借一当二",由18世纪德国数理哲学大师莱布尼兹发现.当前的计算机系统使用的基本上是二进制系统,数据在计算机中主要是以补码的形式存储的.计算机中的二进制则是一个非常微小的开关,用"开"来表示1,"关"来表示0. 那么Java中的二进制又是怎么样的呢?让我们一起来揭开它神

POJ 1753 bfs+位运算

T_T ++运算符和+1不一样.(i+1)%4 忘带小括号了.bfs函数是bool 型,忘记返回false时的情况了.噢....debug快哭了...... DESCRIPTION:求最少的步骤.使得棋盘上的棋子全黑或者全白.奇数次相当于1次.偶数次相当于不翻. bfs用来求解最优问题.主要用来求距离初始状态路径最短的路径.思想是暴力枚举+位运算.第一次做位运算的题目..醉醉的啦...... 附代码: #include<stdio.h>#include<string.h>#incl

位运算/CodeForces 485c Bits

1 /* 2 PROBLEM:CF 485C 3 AUTHER:Nicole 4 MEMO:位运算 二进制 5 */ 6 #include<cstdio> 7 int main() 8 { 9 int n; 10 scanf("%d",&n); 11 for (int i=1;i<=n;i++) 12 { 13 long long l,r; 14 scanf("%lld%lld",&l,&r); 15 long long x

进制与位运算

目录 进制与位运算 二进制,八进制,十六进制和转换 计算机中数的表示 C++的位运算 进制与位运算 二进制,八进制,十六进制和转换 二进制:由0和1组成,"逢二进一". 八进制:由0,1,2,3,4,5,6,7组成,"逢八进一". C/C++中,在数字前加上前缀"0"表示八进制. 十六进制:由0,1,2,3,4,5,6,7,8,9,A(10),B(11),C(12),D(13),E(14),F(15)组成,"逢十六进一". C

【UVA】658 - It&#39;s not a Bug, it&#39;s a Feature!(隐式图 + 位运算)

这题直接隐式图 + 位运算暴力搜出来的,2.5s险过,不是正法,做完这题做的最大收获就是学会了一些位运算的处理方式. 1.将s中二进制第k位变成0的处理方式: s = s & (~(1 << pos)); 将s中二进制第k位变成1的处理方式: s = s | (1 << pos); 2.二进制运算: [1] & : 1 & 1 = 1 , 1 & 0 = 0 , 0 & 0 = 0; 快速判断奇偶性: if(a & 1);//为奇数