NOIP 考前 Tarjan复习

Posted on 2016-10-28 13:51  yyjxx2010xyu  阅读(157)  评论(0编辑  收藏  举报

 

POJ 1236

给定一个有向图,求:

1) 至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点

2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点

第一个就是缩点之后有多少入度为0的个数因为一定要从这些节点出发

第二个就是因为图本身就是联通的,设入度为0的点有n个,出度为0的点有m个,

那么入度的编号为N0,N1,N2,N3...Nn,出度编号为M0,M1,M2...Mm 

那么连N0->M0->N1->M1->N2->M3。这样形成环啦那连到Min(n,m)时就会结束了剩下的Abs(n-m)点,连起来就有Max(n,m)个点啦.

 1 #include <cstring>
 2 const int Maxn=110;
 3 const int Maxm=10010;
 4 struct EDGE{int to,next;}edge[Maxm<<2];
 5 int head[Maxn],Dfn[Maxn],Low[Maxn],Belong[Maxn],In[Maxn],Out[Maxn],Stack[Maxn],Top,vis[Maxn],n,cnt,Stamp,Scc,x;
 6 inline int Max(int x,int y) {return x>y?x:y;}
 7 inline int Min(int x,int y) {return x>y?y:x;}
 8 inline void Add(int u,int v) {edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;}
 9 
10 void Tarjan(int u,int fa)
11 {
12     Low[u]=Dfn[u]=++Stamp; Stack[++Top]=u; vis[u]=true;
13     for (int i=head[u];i!=-1;i=edge[i].next)
14     {
15         int v=edge[i].to;
16         if (v==fa) continue;
17         if (!Dfn[v])
18         {
19             Tarjan(v,u);
20             Low[u]=Min(Low[u],Low[v]);
21         } else if (vis[v]) Low[u]=Min(Low[u],Dfn[v]);
22     }
23     if (Dfn[u]==Low[u])
24     {
25         Scc++; int v=0;
26         while (v!=u)
27         {
28             v=Stack[Top--];
29             vis[v]=false;
30             Belong[v]=Scc;
31         }
32     }
33 }
34 int main()
35 {
36     // freopen("c.in","r",stdin);
37     scanf("%d",&n);
38     memset(head,-1,sizeof(head));
39     for (int i=1;i<=n;i++)
40         while (scanf("%d",&x)&&x)
41             Add(i,x);
42     memset(vis,false,sizeof(vis)); Scc=0;
43     for (int i=1;i<=n;i++) if (!Dfn[i]) Tarjan(i,1);
44     for (int u=1;u<=n;u++)
45         for (int i=head[u];i!=-1;i=edge[i].next)
46             if (Belong[u]!=Belong[edge[i].to]) In[Belong[edge[i].to]]++,Out[Belong[u]]++;
47     int Tmp1=0,Tmp2=0;
48     for (int i=1;i<=Scc;i++)
49     {
50         if (In[i]==0) Tmp1++;
51         if (Out[i]==0) Tmp2++;
52     }
53     if (Scc==1)
54     {
55         puts("1");
56         puts("0");
57         return 0;
58     }
59     printf("%d\n",Tmp1);
60     printf("%d\n",Max(Tmp1,Tmp2));
61     return 0;
62 }
POJ 1236

 

POJ 3177

给定一个连通的无向图G,至少要添加几条边,才能使其变为双连通图。

Tarjan缩点后统计叶子节点的个数,给两个相连就可以了 所以Sum=(Ans+1)>>1;

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <algorithm>
 5 using namespace std;
 6 const int Maxn=5100;
 7 const int Maxm=10010;
 8 struct EDGE{int to,next;}edge[Maxm<<2];
 9 int head[Maxn],Dfn[Maxn],Low[Maxn],vis[Maxn],n,m,u,v,cnt,Top,Stack[Maxn],d[Maxn],Scc,Stamp,Belong[Maxn],Ans;
10 inline void Add(int u,int v) {edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;}
11 inline int Min(int x,int y) {return x>y?y:x;}
12 
13 void Tarjan(int u,int fa)
14 {
15     Dfn[u]=Low[u]=++Stamp; Stack[++Top]=u; vis[u]=true;
16     for (int i=head[u];i!=-1;i=edge[i].next)
17     {
18         int v=edge[i].to;
19         if (i==(fa^1)) continue;
20         if (!Dfn[v])
21         {
22             Tarjan(v,i);
23             Low[u]=Min(Low[u],Low[v]);
24         } else if (vis[v]) Low[u]=Min(Low[u],Dfn[v]);
25     }
26     if (Dfn[u]==Low[u])
27     {
28         Scc++;
29         while (true)
30         {
31             int v=Stack[Top--];
32             vis[v]=false;
33             Belong[v]=Scc;
34             if (v==u) break;
35         }
36     }
37 }
38 int main()
39 {
40     scanf("%d%d",&n,&m);
41     memset(head,-1,sizeof(head));
42     for (int i=1;i<=m;i++) 
43     {
44         scanf("%d%d",&u,&v);
45         Add(u,v),Add(v,u);
46     }
47     for (int i=1;i<=n;i++) if (!Dfn[i]) Tarjan(i,-1);
48     for (int u=1;u<=n;u++)
49     {
50         for (int i=head[u];i!=-1;i=edge[i].next)
51             if (Belong[u]!=Belong[edge[i].to]) d[Belong[u]]++;
52     }
53     for (int i=1;i<=Scc;i++) if (d[i]==1) Ans++;
54     printf("%d\n",(Ans+1)>>1);
55     return 0;
56 }
POJ 3177

 

POJ 1144

给你一个图,求有多少个割点

 u为树根,且u有多于一个子树。 (2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得DFS(u)<=Low(v)。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 const int Maxn=1010;
 7 const int Maxm=2001000;
 8 struct EDGE{int to,next;}edge[Maxm];
 9 int head[Maxn],Dfn[Maxn],Low[Maxn],Root,Son,u,v,n,cnt,Stamp;
10 bool Cut[Maxn];
11 inline int Min(int x,int y) {return x>y?y:x;}
12 inline void Add(int u,int v) {edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;}
13 void Tarjan(int u)
14 {
15     Dfn[u]=Low[u]=++Stamp;
16     for (int i=head[u];i!=-1;i=edge[i].next)
17     {
18         int v=edge[i].to;
19         if (!Dfn[v])
20         {
21             Tarjan(v);
22             if (u==Root) Son++;
23             else
24             {
25                 Low[u]=Min(Low[u],Low[v]);
26                 if (Dfn[u]<=Low[v]) Cut[u]=true;
27             }
28         } else
29             Low[u]=Min(Low[u],Dfn[v]);
30     }
31 }
32 int main()
33 {
34     while (scanf("%d",&n)!=EOF && n!=0)
35     {
36         memset(head,-1,sizeof(head));
37         memset(Dfn,0,sizeof(Dfn));
38         memset(Low,0,sizeof(Low));
39         memset(Cut,false,sizeof(Cut)); Stamp=0; cnt=0;
40         while (scanf("%d",&u)!=EOF && u!=0)
41             while (getchar()!='\n')
42             {
43                 scanf("%d",&v);
44                 Add(u,v),Add(v,u);
45             }
46         Root=1,Son=0;
47         Tarjan(1);
48         int Ans=0;
49         if (Son>1) Ans=1;
50         for (int i=1;i<=n;i++) if (Cut[i]) Ans++;
51         printf("%d\n",Ans);
52     }
53     return 0;
54 }
POJ 1144

 

POJ 2186

求出度为0的奶牛的个数即可

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <algorithm>
 5 using namespace std;
 6 const int Maxn=10100;
 7 const int Maxm=50100;
 8 struct EDGE{int to,next;}edge[Maxm<<1];
 9 int n,m,Dfn[Maxn],Low[Maxn],head[Maxn],Stack[Maxn],Belong[Maxn],Scc,Stamp,cnt,Top,u,v,Out[Maxn],vis[Maxn],Size[Maxn];
10 inline void Add(int u,int v) {edge[cnt].to=v;edge[cnt].next=head[u]; head[u]=cnt++;}
11 inline int Min(int x,int y) {return x>y?y:x;}
12 void Tarjan(int u)
13 {
14     Dfn[u]=Low[u]=++Stamp; Stack[++Top]=u; vis[u]=true;
15     for (int i=head[u];i!=-1;i=edge[i].next)
16     {
17         int v=edge[i].to;
18         if (!Dfn[v])
19         {
20             Tarjan(v);
21             Low[u]=Min(Low[u],Low[v]);
22         } else
23         if (vis[v])
24             Low[u]=Min(Low[u],Dfn[v]);
25     }
26     if (Dfn[u]==Low[u])
27     {
28         Scc++;
29         int v=0;
30         while (v!=u)
31         {
32             v=Stack[Top--];
33             Belong[v]=Scc,++Size[Scc];
34             vis[v]=false;
35         }
36     }
37 }
38 int main()
39 {
40     scanf("%d%d",&n,&m);
41     memset(head,-1,sizeof(head));
42     for (int i=1;i<=m;i++)
43     {
44         scanf("%d%d",&u,&v);
45         Add(u,v);
46     }
47     for (int i=1;i<=n;i++) 
48         if (!Dfn[i]) Tarjan(i);
49     for (int u=1;u<=n;u++)
50     {
51         for (int i=head[u];i!=-1;i=edge[i].next)
52             if (Belong[u]!=Belong[edge[i].to])      Out[Belong[u]]++;
53     }
54     int Zero=0,k=0;
55     for (int i=1;i<=Scc;i++) 
56         if (!Out[i]) 
57         {
58             Zero++;
59             k=Size[i];
60         }
61     Zero>1?puts("0"):printf("%d\n",k);
62     return 0;
63 }
POJ2186

 

POJ 3352

一个连通的无向图,求至少需要添加几条边,救能保证删除任意一条边,图仍然是连通的,即成为双连通分量

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <algorithm>
 5 using namespace std;
 6 const int Maxn=1100;
 7 const int Maxm=10010;
 8 struct EDGE{int to,next;}edge[Maxm<<2];
 9 int head[Maxn],Dfn[Maxn],Low[Maxn],vis[Maxn],n,m,u,v,cnt,Top,Stack[Maxn],d[Maxn],Scc,Stamp,Belong[Maxn],Ans;
10 inline void Add(int u,int v) {edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;}
11 inline int Min(int x,int y) {return x>y?y:x;}
12 void Tarjan(int u,int fa)
13 {
14     Dfn[u]=Low[u]=++Stamp; Stack[++Top]=u; vis[u]=true;
15     for (int i=head[u];i!=-1;i=edge[i].next)
16     {
17         int v=edge[i].to;
18         if (v==fa) continue;
19         if (!Dfn[v] && !vis[v])
20         {
21             Tarjan(v,u);
22             Low[u]=Min(Low[u],Low[v]);
23         } else if (vis[v]) Low[u]=Min(Low[u],Dfn[v]);
24     }
25     if (Dfn[u]==Low[u])
26     {
27         Scc++; 
28         while (Stack[Top]!=u && Top>=1) Belong[Stack[Top--]]=Scc;
29         Belong[Stack[Top--]]=Scc;
30     }
31 }
32 int main()
33 {
34     scanf("%d%d",&n,&m);
35     memset(head,-1,sizeof(head));
36     for (int i=1;i<=m;i++) 
37     {
38         scanf("%d%d",&u,&v);
39         Add(u,v),Add(v,u);
40     }
41     Tarjan(1,0);
42     for (int u=1;u<=n;u++)
43     {
44         for (int i=head[u];i!=-1;i=edge[i].next)
45             if (Belong[u]!=Belong[edge[i].to]) d[Belong[edge[i].to]]++;
46     }
47     
48     for (int i=1;i<=Scc;i++) if (d[i]==1) Ans++;
49     printf("%d\n",(Ans+1)>>1);
50     return 0;
51 }
POJ 3352

 

HDU 3394

 一个无向图(可能不连通)有n个点和m条边.现在要你找出冲突边和多余边的数目.其中冲突边是那些同时存在于多个环中的边,而多余边是不在任何一个环中的边.当点数=边数,形成一个环,当点数>边数(一条线段,说明这条边是桥),当点数<边数,那么就含1个以上的环了

 1 #include <cstdio>
 2 #include <cstring>
 3 const int Maxn=10100;
 4 const int Maxm=100100;
 5 const int Inf=0x3f3f3f3f;
 6 int Low[Maxn],Dfn[Maxn],Bcc[Maxn],vis[Maxn],Stamp,Top,cnt,u,v,Bridge,Way;
 7 int head[Maxn],Stack[Maxn],n,m,belong[Maxn];
 8 struct EDGE{int to,next;}edge[Maxm<<2];
 9 inline int Min(int x,int y) {return x>y?y:x;}
10 inline void Add(int u,int v)
11 {edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;}
12 inline void CirCuit()
13 {
14     int Cnt=0;
15     memset(belong,false,sizeof(belong));
16     for (int i=1;i<=Bcc[0];i++) belong[Bcc[i]]=true;
17     for (int j=1;j<=Bcc[0];j++)
18     {
19         int u=Bcc[j];
20         for (int i=head[u];i!=-1;i=edge[i].next)
21             if (belong[edge[i].to]) Cnt++;
22     }
23     Cnt>>=1;
24     if (Cnt>Bcc[0]) Way+=Cnt;
25 }
26 void Tarjan(int u,int fa)
27 {
28     Low[u]=Dfn[u]=++Stamp; Stack[++Top]=u; vis[u]=true;
29     for (int i=head[u];i!=-1;i=edge[i].next)
30     {
31         int v=edge[i].to;
32         if (fa==(i^1)) continue;
33         if (!Dfn[v])
34         {
35             Tarjan(v,i);
36             Low[u]=Min(Low[u],Low[v]);
37             if (Low[v]>=Dfn[u])
38             {
39                 if (Low[v]>Dfn[u]) Bridge++;
40                 Bcc[0]=0;
41                 while (true)
42                 {
43                     int x=Stack[Top--];
44                     Bcc[++Bcc[0]]=x;
45                     if (x==v) break;
46                 }
47                 Bcc[++Bcc[0]]=u;
48                 CirCuit();
49             }
50         } else  if (vis[v]) Low[u]=Min(Low[u],Dfn[v]);
51     }
52 }
53 int main()
54 {
55     while (scanf("%d%d",&n,&m)!=EOF)
56     {
57         if (n==0 && m==0) break;
58         cnt=0; memset(head,-1,sizeof(head));
59         memset(Dfn,0,sizeof(Dfn)); Top=0; 
60         memset(Low,0,sizeof(Low)); Stamp=0;
61         memset(vis,false,sizeof(vis));
62         Bridge=Way=0;
63         for (int i=1;i<=m;i++)
64         {
65             scanf("%d%d",&u,&v);
66             Add(u,v),Add(v,u);
67         }
68         for (int i=1;i<=n;i++) if (!Dfn[i]) Tarjan(i,-1);
69         printf("%d %d\n",Bridge,Way);
70     }
71     return 0;
72 }
HDU 3394