Cutting Chains UVA - 818(二进制枚举+dfs)

在这里插入图片描述

题目大意:
给你n个环,并且有些环是已经连接了的,例如样例
5 1 2 2 3 3 4 5
1和2已经连接,2和3已经连接,3和4已经连接,4和5已经连接,问你让他们组成一个链最少需要打开的次数,这时123,45组成了两个环,这时我们只需要把4打开链接3和5然后再次关闭,打开次数为1,或者打开3也是一样的。
思路:
https://www.cnblogs.com/dwtfukgv/p/5601681.html
看了这篇题解后勉强写出来的,个人感觉是比较难的,它考了对二进制子集的深刻理解,以及很多细节问题(都不太好处理)。
总体来说,因为n最大值为15,那么它的全集是2^15=32768然后在这些子集中找到最小解,显然n=15不至于超时,所以暴力二进制枚举是可行的,我们把他的全部子集枚举出来然后选最小的那个值就行了。
然后还有几个细节需要处理的,二进制中0表示环中第i个元素不拆开,1表示环中第i个元素拆开,最差的情况就是有n个元素,然后你把n-1个元素全部拆开然后组成链,那么不拆的时候我们需要判断不拆的元素是否会构成环,如果构成环显然是不可能构成链了,或者没有环但是不拆中的某个元素他的出度>2,想象一下,显然这种情况也是不可能再构成环的。还有如果拆的次数>=分支-1才可以,想象一个已经存在三个分支,但是你只打开了一个环,那么只能连接两个分支。
代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn=25;
int g[maxn][maxn];
int num,n;
int vis[maxn];
bool branch(int s){
	for(int i=0;i<n;i++){//最多又n个0 1 状态0表示不拆 1 表示拆 
		if(s&(1<<i))continue;//表示当前的环是要拆的,不需要考虑出度
		int deg=0;//否则就是第i个不拆,那么检查第i个出度是否大于2 
		for(int j=0;j<n;j++){
			if(s&(1<<j))continue;//当前不拆但是第j个环是要拆的,不计入出度 
			if(g[i][j])
			deg++;
		}
		if(deg>2)
		return true;//存在环中某个元素不拆并且出度大于2显然不可能组成链 
	}
	return false;
}
bool dfs(int s,int now,int 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 chain(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 chai(int s){
	int _n=0;
	for(int i=0;i<n;i++){
		if(s&(1<<i))
		_n++;
	}
	return _n;
}
int solve(){
	int ans=n-1;//最多拆n-1次 
	for(int i=0;i<(1<<n);i++){
		num=0;
		memset(vis,0,sizeof(vis));
		if(branch(i)||chain(i))continue;//如果存在环或者出度大于2 
		if(chai(i)>=num-1)ans=min(ans,chai(i)); 
	}
	return ans;
}
int main(){
	int k=1,a,b;
	while(scanf("%d",&n)&&n){
		memset(g,0,sizeof(g));
		while(scanf("%d%d",&a,&b)){
			if(a==-1&&b==-1)break;
			g[a-1][b-1]=g[b-1][a-1]=1;
		}
		printf("Set %d: Minimum links to open is %d\n",k++,solve());
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值