给出环的数量n,之后再给出环的连接状态,问最少打开几个环再重新连接就能使所有环连成一个链条。
环的数量很少,用二进制枚举子集,然后逐个判断是否可以连成一条链,如果连成,用解开的数目维护最小值。
判断能否连成一条链,首先要使解开后的链条没有三个连在一起的结点,然后链条不能形成环。
判断形成环时,DFS判断。若从该结点出发,能回到该结点,则有环。
今天在看代码时发现个可以优化的地方,加了个回溯条件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| #include<cstdio> #include<cstring> const int INF=1<<30; const int maxn=20; int n,best; bool g[maxn][maxn],vis[maxn]; bool read(){ if(scanf("%d",&n)==EOF||!n) return false; memset(g,0,sizeof(g)); int u,v; while(~scanf("%d%d",&u,&v)&&!(u==-1&&v==-1)) g[u-1][v-1]=g[v-1][u-1]=true; return true; } bool node(int s){ for(int i=0;i<n;++i){ if(s&(1<<i)) continue; int num=0; for(int j=0;j<n;++j) if(!(s&(1<<j))&&g[i][j]) ++num; if(num>2) return false; } return true; } bool dfs(int s,int cur,int pre) { vis[cur]=true; bool ok=true; for(int i=0;i<n;++i){ if(!(s&(1<<i))&&g[cur][i]){ if(!vis[i]){ if(!dfs(s,i,cur)) ok=false; } else if(i!=pre) return false; } } if(!ok) return false; return true; } bool ring(int s,int num){ int cnt=0; memset(vis,0,sizeof(vis)); for(int i=0;i<n;++i){ if(s&(1<<i)) continue; if(vis[i]) continue; ++cnt; if(!dfs(s,i,-1)) return false; } if(cnt>num+1) return false; return true; } int solve(){ int best=INF,s=1<<n; for(int i=0;i<s;++i){ int cur=0; for(int j=0;j<n;++j) if(i&(1<<j)) ++cur; if(cur>=best) continue; if(node(i)&&ring(i,cur)) best=best>cur?cur:best; } return best; } int main(){ int t=0; while(read()) printf("Set %d: Minimum links to open is %d\n",++t,solve()); return 0; }
|
** 本文迁移自我的CSDN博客,格式可能有所偏差。 **