二進制枚舉
紫書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;
}