http://poj.org/problem?id=1523
居然1A and 0ms 太让我意外了
题目大意:
给你几个电脑的双相连通图 问你是否存在割点 如果存在输出割点并输出此割点见原图变成了几个块
输入输出只要注意就是了 没别的办法
Tarjan 算法 我就不多说了 我也说不好
总之用Tarjan算法找割点 但是你搜索时的根结点要特判
对不每个割点用dfs求其可把原图分成几个块
从割点发出可能用k个分支 那么块数 <= k
对其分支进行深搜并记录 有几个分支可以向下搜 就有几个块
深搜记录 可以搞定几个分支相连的情况
详情见代码注释:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<stack> #include<algorithm> using namespace std; const int N=1005; const int M=10000005; struct node { struct tt *next; }mem[N]; struct tt { struct tt *next; int j; }; int had[N];//割点是否已保存 int cutpoint[N];//保存割点 int I;//割点数目 int deep;//深度 (时间戳) bool visited[N]; stack<int>str; bool in[N]; int low[N]; int dfn[N]; int st;//开始搜的起始点 void build(int i,int j) { struct tt *t=new tt; t->j=j; t->next=mem[i].next; mem[i].next=t; } void Clear()//每次对表头进行清理 { for(int i=0;i<N;++i) mem[i].next=NULL; } void Tarjan(int x) { ++deep; low[x]=dfn[x]=deep; visited[x]=true; str.push(x); in[x]=true; struct tt *t=mem[x].next; while(t!=NULL) { if(visited[t->j]==false) { Tarjan(t->j); low[x]=min(low[x],low[t->j]); }else if(in[t->j]) { low[x]=min(low[x],dfn[t->j]); } if(!had[x]&&low[t->j]>=dfn[x]&&x!=st)//如果此点没记录 且为割点 {//但是不能等于起始点 因为起始点得进行特判 had[x]=true; cutpoint[I]=x; ++I; } t=t->next; } if(low[x]==dfn[x])//对以x为根的强连通图进行出栈处理 { while(str.top()!=x) { in[str.top()]=false; str.pop(); } in[str.top()]=false; str.pop(); } } void dfs(int x)//深搜并记录 { visited[x]=true; struct tt *t=mem[x].next; while(t!=NULL) { if(visited[t->j]==false) dfs(t->j); t=t->next; } } int findnum(int x) { memset(visited,false,sizeof(visited)); visited[x]=true; struct tt *t=mem[x].next; int num=0; while(t!=NULL) { if(visited[t->j]==false)//有多少分支是可搜的 { ++num; dfs(t->j); } t=t->next; } return num; } int main() { int i,j; for(int w=1;;++w) { st=M; while(scanf("%d",&i),i) { scanf("%d",&j); if(st==M) st=j; build(i,j); build(j,i); } if(st==M) break; I=0; if(findnum(st)>1)//对起始点进行特判 看它有多少不相连分支 { had[st]=true; cutpoint[I]=st; ++I; } deep=0; while(!str.empty()) str.pop(); memset(in,false,sizeof(in)); memset(visited,false,sizeof(visited)); memset(had,false,sizeof(had)); Tarjan(st); printf("Network #%d\n",w); if(I==0) { printf(" No SPF nodes\n"); } else { sort(cutpoint,cutpoint+I); for(int i=0;i<I;++i) { printf(" SPF node %d leaves %d subnets\n",cutpoint[i],findnum(cutpoint[i])); } } printf("\n"); Clear(); } return 0; }
更令我意外的是 直接用dfs 就能过 时间 16ms
每次对每个点进行判定 如果从他出发的分支有多余一个是可搜的 则是割点 且有多少是可搜的 就有多少块
其实就是对上面程序中对起始点特判的方法 无语了
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<stack> #include<algorithm> using namespace std; const int N=1001; const int M=10000005; struct node { struct tt *next; }mem[N]; struct tt { struct tt *next; int j; }; int I;//割点数目 bool visited[N]; bool had[N]; void build(int i,int j) { struct tt *t=new tt; t->j=j; t->next=mem[i].next; mem[i].next=t; } void Clear()//每次对表头进行清理 { for(int i=0;i<N;++i) mem[i].next=NULL; } void dfs(int x)//深搜并记录 { visited[x]=true; struct tt *t=mem[x].next; while(t!=NULL) { if(visited[t->j]==false) dfs(t->j); t=t->next; } } int findnum(int x) { memset(visited,false,sizeof(visited)); visited[x]=true; struct tt *t=mem[x].next; int num=0; while(t!=NULL) { if(visited[t->j]==false)//有多少分支是可搜的 { ++num; dfs(t->j); } t=t->next; } // cout<<x<<" "<<num<<endl; return num; } int main() { int i,j; for(int w=1;;++w) { memset(had,false,sizeof(had)); j=-1; while(scanf("%d",&i),i) { scanf("%d",&j); had[i]=had[j]=true; build(i,j); build(j,i); } if(j==-1) break; printf("Network #%d\n",w); I=0; for(int i=1;i<N;++i) { if(had[i]==false) continue; int k=findnum(i); if(k>1) { ++I; printf(" SPF node %d leaves %d subnets\n",i,k); } } if(I==0) { printf(" No SPF nodes\n"); } printf("\n"); Clear(); } return 0; }