本题虽然解题方法简单,但是一直wrong了很多遍。
首先,要用最少的open来拼成链状,直接暴力枚举(2^n)种可能性,每种的判断,是关键,首先把这些open的环拿出,剩下的不能有环而且不能存在度数大于2的节点。
最后一个条件就是,剩下的联通分量要不超过open环个数加1。剩下就是暴了。
首先,错误之处,对dfs时数组的更改,一定要使用记忆数组;
判断有没有环只需把每条边正反存两遍,dfs求是否碰到标记点。
判断度数,直接统计边就行了。
#include <cstdio> #include <algorithm> #include <iostream> #include <cstring> #include <queue> using namespace std; const int maxn = 16; int ma[maxn][maxn],te[maxn][maxn],n; int judge_inout(){ int c[maxn]={0}; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ if(te[i][j]){ if(++c[i]>=5) return 0; if(++c[j]>=5) return 0; } } return 1; } int vis[maxn],have; int hav(int u,int fa){ vis[u] = 1; for(int v=1;v<=n;v++) if(v!=u && v!=fa){ if(te[u][v]||te[v][u]){ if(vis[v]){ have = 1; } else hav(v,u); } } } void copy(){ for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) te[i][j] = ma[i][j]; } int res ; void Dfs(int p,int d){ if(d>res) return ; if(p==n+1){ if(judge_inout()){ int cnt = 0,ok=1; memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++){ if(!vis[i]){ cnt++; have = 0; hav(i,-1); if(have){ ok = 0; } } } if(ok){ cnt-=d; if(cnt<=d+1) { res = d; } } } return ; } Dfs(p+1,d); int tem[2][n]; for(int i=1;i<=n;i++){ tem[0][i]=te[p][i]; tem[1][i]=te[i][p]; te[p][i]=te[i][p]=0; } Dfs(p+1,d+1); for(int i=1;i<=n;i++){ te[p][i]=tem[0][i];//不可以悔改为ma[p][i] ,若前一步已更改改点,ma[ p][i]便没体现该信息; te[i][p]=tem[1][i]; } } int main() { int kase=1; while(scanf("%d",&n)==1&&n){ memset(ma,0,sizeof(ma)); int x,y; while(scanf("%d %d",&x,&y)&&x!=-1){ ma[x][y]=ma[y][x]=1; } res = n; copy(); memset(vis,0,sizeof(vis)); Dfs(1,0); printf("Set %d: Minimum links to open is %d\n",kase++,res); } return 0; }