二進制枚舉 學習筆記

二進制枚舉

紫書215頁暴力求解法裏面有一道題目(Cutting Chains UVA - 818 )要用到二進制枚舉,所以學了一下;

總結來說就是對n個事件(n<32);每個事件都有兩種情況,所以可以用0和1來表示事件的發生和不發生,每個事件的序號又可以和二進制位相對應,所以全部n事件的狀態,可以用1到2^n的數字的二進制來表示;

然後根據一個數的二進制0和1的狀態來判斷是否符合條件,是一個不錯的暴力算法;

題目:

Cutting Chains

這道題就是枚舉每個圓環,每個圓環有兩種狀態,開和閉,然後根據每次枚舉的狀態來判斷是否符合題目要求;

代碼:

#include<bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#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;
}
發佈了207 篇原創文章 · 獲贊 45 · 訪問量 8305
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章