Description
Input
Output
Sample Input
5 2 4 3 0 4 5 0 0 0 1 0
Sample Output
1 2
开始用桥来判断是不是同一个连通分量,结果果断错了,其实下图应该就会出错
原因是通过头插法先遍历3,结果3的出度为0,由于2通向3已经访问过,因此不能在访问,因此2-->3的路没有标志cut,没法统计这天边的出入度情况,因此出度为0的变为2个了,正确答案应该是1个,所以错了,不能企图通过桥来算出出度入度
错误代码:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define MOD 100000 #define inf 1<<29 #define LL long long #define MAXN 20010 #define MAXM = 50010 using namespace std; struct Edge { int to,next; bool cut; } edge[MAXN]; int head[MAXN],tot; int low[MAXN],DFN[MAXN],belong[MAXN];///belong 的值为1-block int index,top,fenzhiNum; int block ; ///强连通分量 bool inStack[MAXN]; int bridgeNum; ///桥的数目 int stack[MAXN]; int vis[MAXN]; int inans,outans; int outdu[MAXN]; int indu[MAXN]; void addedge(int u,int v) { edge[tot].to = v; edge[tot].next = head[u]; edge[tot].cut = false; head[u] = tot++ ; } void ini(){ index = block = top = fenzhiNum = 0; inans = 0, outans = 0 ; memset(DFN,0,sizeof(DFN)); memset(inStack,false,sizeof(inStack)); memset(vis,0,sizeof(vis)); memset(outdu,0,sizeof(outdu)); memset(indu,0,sizeof(indu)); } void Tarjan(int u) { vis[u] = true; int v; low[u] = DFN[u] = ++index; stack[top++] = u; inStack[u] = true; for(int i=head[u] ; i!=-1 ; i=edge[i].next) { v = edge[i].to; //if( v == pre ) continue; ///因为是无向图,所以两条是双向的,所以只遍历一条就够了 if( !DFN[v] ) { Tarjan(v ); if(low[u]>low[v]) low[u] = low[v]; if(low[v] > DFN[u] ){ bridgeNum++; edge[i].cut = true; //edge[i^1].cut = true; ///将两条双向边都设置为桥 } } else if( inStack[v] && low[u] > DFN[v]) low[u] = DFN[v]; } if(low[u] == DFN[u]) { block++; do { v=stack[--top]; ///清空当前强连通分量栈 必须清空 inStack[v] = false; belong[v]=block; ///v节点都编号为block 也就是这是一个块 } while(v!=u); } } void solve(int N) { ini(); for(int i=1;i<=N;i++) if(!vis[i]) Tarjan(i); for(int i=1; i<=N ; i++){ ///缩点 for(int j=head[i] ; j!=-1 ; j=edge[j].next) if( edge[j].cut)//belong[i]!=belong[ edge[j].to ])//edge[j].cut ) indu[ belong[ edge[j].to ] ]++,outdu[ belong[i] ]++ ; } for(int i=1;i<=block ;i++) if(indu[i] == 0) inans++; for(int i=1;i<=block ;i++) if(outdu[i] == 0) outans++; // printf("indu=%d,outdu=%d\n",inans,outans); if(block == 1) printf("1\n0\n"); else printf("%d\n%d\n",inans,max(inans,outans)); //printf("%d\n",(ans+1)/2 ); } int main () { int n,m; while(~scanf("%d",&n)) { int u,v,mark=0; tot=0; memset(head,-1,sizeof(head)); for(int i=1; i<=n; i++) { while(scanf("%d",&u)&&u!=0){ mark=0; for(int j=head[i] ; j!=-1 ; j=edge[j].next) ///去重边 if(edge[j].to == u){ mark = 1; break; } if(!mark) addedge(i,u); } } solve(n); } return 0; }
正确代码矩阵:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define MAXV 110 #define min(a,b) (a>b?b:a) #define max(a,b) (a>b?a:b) int n,map[MAXV][MAXV],outdegree[MAXV],indegree[MAXV]; int dfn[MAXV]; //第一次访问的步数 int low[MAXV]; //子树中最早的步数 int stap[MAXV],stop; //模拟栈 bool instack[MAXV]; //是否在栈中 int count; //记录连通分量的个数 int cnt; //记录搜索步数 int belong[MAXV]; //属于哪个连通分量 void init(){ count=stop=cnt=0; memset(instack,false,sizeof(instack)); memset(map,0,sizeof(map)); memset(dfn,0,sizeof(dfn)); } void tarjan(int x){ int i; dfn[x]=low[x]=++cnt; stap[stop++]=x; instack[x]=true; for(i=1;i<=n;i++){ if(!map[x][i]) continue; if(!dfn[i]){ tarjan(i); low[x]=min(low[i],low[x]); }else if(instack[i]) low[x]=min(dfn[i],low[x]); //与x相连,但是i已经被访问过,且还在栈中 //用子树节点更新节点第一次出现的时间 } if(low[x]==dfn[x]){ count++; while(1){ int tmp=stap[--stop]; belong[tmp]=count; instack[tmp]=false; if(tmp==x) break; } } } void output(){ int i,j,inzero=0,outzero=0; for(i=1;i<=n;i++){ indegree[i]=outdegree[i]=0; } for(i=1;i<=n;i++) //找连通分量入度与出度 for(j=1;j<=n;j++) if(map[i][j] && belong[i]!=belong[j]){ indegree[belong[j]]++; outdegree[belong[i]]++; } for(i=1;i<=count;i++){ //找入度与出度为0的点 if(!indegree[i]) inzero++; if(!outdegree[i]) outzero++; } if(count==1) //只有1个结点要特判 printf("1\n0\n"); else printf("%d\n%d\n",inzero,max(inzero,outzero)); } int main(){ int i,a; while(~scanf("%d",&n)){ init(); for(i=1;i<=n;i++){ while(scanf("%d",&a) && a) map[i][a]=1; } for(i=1;i<=n;i++) if(!dfn[i]) tarjan(i); output(); } return 0; }
正确代码邻接表
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define MOD 100000 #define inf 1<<29 #define LL long long #define MAXN 20010 #define MAXM = 50010 using namespace std; struct Edge { int to,next; bool cut; } edge[MAXN]; int head[MAXN],tot; int low[MAXN],DFN[MAXN],belong[MAXN];///belong 的值为1-block int index,top,fenzhiNum; int block ; ///强连通分量 bool inStack[MAXN]; int bridgeNum; ///桥的数目 int stack[MAXN]; int vis[MAXN]; int inans,outans; int outdu[MAXN]; int indu[MAXN]; void addedge(int u,int v) { edge[tot].to = v; edge[tot].next = head[u]; edge[tot].cut = false; head[u] = tot++ ; } void ini(){ index = block = top = fenzhiNum = 0; inans = 0, outans = 0 ; memset(DFN,0,sizeof(DFN)); memset(inStack,false,sizeof(inStack)); memset(vis,0,sizeof(vis)); memset(outdu,0,sizeof(outdu)); memset(indu,0,sizeof(indu)); } void Tarjan(int u) { vis[u] = true; int v; low[u] = DFN[u] = ++index; stack[top++] = u; inStack[u] = true; for(int i=head[u] ; i!=-1 ; i=edge[i].next) { v = edge[i].to; //if( v == pre ) continue; ///因为是无向图,所以两条是双向的,所以只遍历一条就够了 if( !DFN[v] ) { Tarjan(v ); if(low[u]>low[v]) low[u] = low[v]; if(low[v] > DFN[u] ){ bridgeNum++; edge[i].cut = true; //edge[i^1].cut = true; ///将两条双向边都设置为桥 } } else if( inStack[v] && low[u] > DFN[v]) low[u] = DFN[v]; } if(low[u] == DFN[u]) { block++; do { v=stack[--top]; ///清空当前强连通分量栈 必须清空 inStack[v] = false; belong[v]=block; ///v节点都编号为block 也就是这是一个块 } while(v!=u); } } void solve(int N) { ini(); for(int i=1;i<=N;i++) if(!vis[i]) Tarjan(i); for(int i=1; i<=N ; i++){ ///缩点 for(int j=head[i] ; j!=-1 ; j=edge[j].next) if( belong[i]!=belong[ edge[j].to ] ) indu[ belong[ edge[j].to ] ]++,outdu[ belong[i] ]++ ; } for(int i=1;i<=block ;i++) if(indu[i] == 0) inans++; for(int i=1;i<=block ;i++) if(outdu[i] == 0) outans++; // printf("indu=%d,outdu=%d\n",inans,outans); if(block == 1) printf("1\n0\n"); else printf("%d\n%d\n",inans,max(inans,outans)); //printf("%d\n",(ans+1)/2 ); } int main () { int n,m; while(~scanf("%d",&n)) { int u,v,mark=0; tot=0; memset(head,-1,sizeof(head)); for(int i=1; i<=n; i++) { while(scanf("%d",&u)&&u!=0){ mark=0; for(int j=head[i] ; j!=-1 ; j=edge[j].next) ///去重边 if(edge[j].to == u){ mark = 1; break; } if(!mark) addedge(i,u); } } solve(n); } return 0; }