二进制枚举 学习笔记

二进制枚举

紫书215页暴力求解法里面有一道题目(Cutting Chains UVA - 818 )要用到二进制枚举,所以学了一下;

总结来说就是对n个事件(n<32);每个事件都有两种情况,所以可以用0和1来表示事件的发生和不发生,每个事件的序号又可以和二进制位相对应,所以全部n事件的状态,可以用1到2^n的数字的二进制来表示;

然后根据一个数的二进制0和1的状态来判断是否符合条件,是一个不错的暴力算法;

题目:

Cutting Chains

这道题就是枚举每个圆环,每个圆环有两种状态,开和闭,然后根据每次枚举的状态来判断是否符合题目要求;

代码:

#include
#define ll long long
#define pa pair
#define lson k<<1
#define rson k<<1|1
//ios::sync_with_stdio(false);
using namespace std;
const int N=100100;
const int M=200100;
const ll mod=1e9+7;
int n;
int ma[20][20],a[20][20];
int vis[20],d[20];
int sum(int p){
	int ans=0;
	while(p){
		ans+=p%2;
		p/=2;
	}
	return ans;
}
void dfs(int p,int q){
	for(int i=0;i<n;i++){
		if(a[p][i]&&i!=q){
			vis[i]++;
			if(vis[i]>2) continue;
			dfs(i,p);
		}
	}
}
bool judge(int p){
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++) a[i][j]=ma[i][j];
	}
	memset(vis,0,sizeof(vis));
	memset(d,0,sizeof(d));
	int b=0,c=0;//打开的圆环数,剩余的支链数 
	for(int i=0;i<n;i++){
		if((1<<i)&p){//打开的圆环 
			b++;
			for(int j=0;j<n;j++) a[i][j]=a[j][i]=0;//跟打开的圆环相连的都断开 
		}
	}
	for(int i=0;i<n;i++){
		if(!((1<<i)&p)){//没打开的圆环 
			for(int j=0;j<n;j++){
				if(a[i][j]) d[i]++;
			}
			if(d[i]>2) return false;
		}
	} 
	for(int i=0;i<n;i++){
		if(!vis[i]&&!((1<<i)&p)){
			c++;
			vis[i]++;
			dfs(i,-1);
		}
	}
	for(int i=0;i<n;i++){
		if(vis[i]>=2) return false;//存在环 
	}
	if(c-1>b) return false;
	return true;
}
int main(){
//    ios::sync_with_stdio(false);
	int c=0;
    while(cin>>n&&n){
    	memset(ma,0,sizeof(ma));
    	int p,q;
    	while(1){
    		cin>>p>>q;
    		if(p==-1||q==-1) break;
    		ma[p-1][q-1]=ma[q-1][p-1]=1;
		}
		int ans=1e9;
		for(int i=0;i<(1<<n);i++){//二进制枚举 
			if(judge(i)) ans=min(ans,sum(i));
		}
		printf("Set %d: Minimum links to open is %d\n",++c,ans);
	}
    return 0;
}

你可能感兴趣的:(#,紫书笔记,#,位运算)