二进制枚举
紫书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;
}