本题虽然解题方法简单,但是一直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;
}