专题训练之强连通分量和2-sat

Posted jackyan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了专题训练之强连通分量和2-sat相关的知识,希望对你有一定的参考价值。

tarjan模板

技术分享图片
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=20010;
 6 const int maxm=50010;
 7 struct edge{
 8     int to,nxt;
 9 }edge[maxm];
10 int head[maxn],tot;
11 int low[maxn],dfn[maxn],stack[maxn],belong[maxn];
12 int index,top;
13 int scc;
14 bool vis[maxn];
15 int num[maxn];
16 
17 void addedge(int u,int v)
18 {
19     edge[tot].to=v;
20     edge[tot].nxt=head[u];
21     head[u]=tot++;
22 }
23 
24 void tarjan(int u)
25 {
26     int v;
27     low[u]=dfn[u]=++index;
28     stack[top++]=u;
29     vis[u]=true;
30     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
31         v=edge[i].to;
32         if ( !dfn[v] ) {
33             tarjan(v);
34             low[u]=min(low[u],low[v]);
35         }
36         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
37     }    
38     if ( low[u]==dfn[u] ) {
39         scc++;
40         do {
41             v=stack[--top];
42             vis[v]=false;
43             belong[v]=scc;
44             num[scc]++;
45         }
46         while ( v!=u );
47     }
48 } 
49 
50 void solve(int N)
51 {
52     memset(dfn,0,sizeof(dfn));
53     memset(vis,false,sizeof(vis));
54     memset(num,0,sizeof(num));
55     index=scc=top=0;
56     for ( int i=1;i<=N;i++ ) {
57         if ( !dfn[i] ) tarjan(i);
58     }
59 }
60 
61 void init()
62 {
63     tot=0;
64     memset(head,-1,sizeof(head));
65 }
tarjan模板

 

1.(HDOJ1269)http://acm.hdu.edu.cn/showproblem.php?pid=1269

分析:裸题,判断scc是否为1即可

技术分享图片
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=1e4+10;
 6 const int maxm=1e5+10;
 7 struct edge{
 8     int to,nxt;
 9 }edge[maxm];
10 int head[maxn],tot;
11 int low[maxn],dfn[maxn],stack[maxn],belong[maxn];
12 int index,top;
13 int scc,n;
14 bool vis[maxn];
15 int num[maxn];
16 
17 void addedge(int u,int v)
18 {
19     edge[tot].to=v;
20     edge[tot].nxt=head[u];
21     head[u]=tot++;
22 }
23 
24 void tarjan(int u)
25 {
26     int v;
27     low[u]=dfn[u]=++index;
28     stack[top++]=u;
29     vis[u]=true;
30     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
31         v=edge[i].to;
32         if ( !dfn[v] ) {
33             tarjan(v);
34             low[u]=min(low[u],low[v]);
35         }
36         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
37     }    
38     if ( low[u]==dfn[u] ) {
39         scc++;
40         do {
41             v=stack[--top];
42             vis[v]=false;
43             belong[v]=scc;
44             num[scc]++;
45         }
46         while ( v!=u );
47     }
48 } 
49 
50 void solve()
51 {
52     memset(dfn,0,sizeof(dfn));
53     memset(vis,false,sizeof(vis));
54     memset(num,0,sizeof(num));
55     index=scc=top=0;
56     for ( int i=1;i<=n;i++ ) {
57         if ( !dfn[i] ) tarjan(i);
58     }
59 }
60 
61 void init()
62 {
63     tot=0;
64     memset(head,-1,sizeof(head));
65 }
66 
67 int main()
68 {
69     int m,i,j,k,x,y;
70     while ( scanf("%d%d",&n,&m)!=EOF && (n+m) ) {
71         init();
72         for ( i=0;i<m;i++ ) {
73             scanf("%d%d",&x,&y);
74             addedge(x,y);
75         }
76         solve();
77         if ( scc==1 ) printf("Yes\n");
78         else printf("No\n");
79     }
80     return 0;
81  } 
HDOJ1269

 

2.(HDOJ1827)http://acm.hdu.edu.cn/showproblem.php?pid=1827

分析:利用tarjan进行缩点得到新的GAD图,然后再根据新图中每个节点的入度和出度进行相应的操作。因为该题需要花费更少,所有只需要求那些所有入度为0的点所需要花费的费用。而对于新图中每个点的花费,可以很方便的在tarjan中进行更新

技术分享图片
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 const int maxn=1005;
  6 const int maxm=2005;
  7 const int inf=1e9;
  8 struct edge{
  9     int to,nxt;
 10 }edge[maxm];
 11 int head[maxn],tot;
 12 int low[maxn],dfn[maxn],stack[maxn],belong[maxn];
 13 int index,top;
 14 int scc,n;
 15 bool vis[maxn];
 16 int num[maxn];
 17 int cost[maxn],in[maxn],out[maxn],cost_[maxn];
 18 
 19 void addedge(int u,int v)
 20 {
 21     edge[tot].to=v;
 22     edge[tot].nxt=head[u];
 23     head[u]=tot++;
 24 }
 25 
 26 void tarjan(int u)
 27 {
 28     int v;
 29     low[u]=dfn[u]=++index;
 30     stack[top++]=u;
 31     vis[u]=true;
 32     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
 33         v=edge[i].to;
 34         if ( !dfn[v] ) {
 35             tarjan(v);
 36             low[u]=min(low[u],low[v]);
 37         }
 38         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
 39     }    
 40     if ( low[u]==dfn[u] ) {
 41         scc++;
 42         do {
 43             v=stack[--top];
 44             vis[v]=false;
 45             belong[v]=scc;
 46             num[scc]++;
 47             cost_[scc]=min(cost_[scc],cost[v]);
 48         }
 49         while ( v!=u );
 50     }
 51 } 
 52 
 53 void solve()
 54 {
 55     memset(dfn,0,sizeof(dfn));
 56     memset(vis,false,sizeof(vis));
 57     memset(num,0,sizeof(num));
 58     for ( int i=1;i<=n;i++ ) cost_[i]=inf;
 59     index=scc=top=0;
 60     for ( int i=1;i<=n;i++ ) {
 61         if ( !dfn[i] ) tarjan(i);
 62     }
 63 }
 64 
 65 void init()
 66 {
 67     tot=0;
 68     memset(head,-1,sizeof(head));
 69 }
 70 
 71 int main()
 72 {
 73     int m,i,j,k,x,y,ans,cnt,v;
 74     while ( scanf("%d%d",&n,&m)!=EOF ) {
 75         init();
 76         for ( i=1;i<=n;i++ ) scanf("%d",&cost[i]);
 77         for ( i=1;i<=m;i++ ) {
 78             scanf("%d%d",&x,&y);
 79             addedge(x,y);
 80         }
 81         solve();
 82         memset(in,0,sizeof(in));
 83         memset(out,0,sizeof(out));
 84         for ( i=1;i<=n;i++ ) {
 85             for ( j=head[i];j!=-1;j=edge[j].nxt ) {
 86                 v=edge[j].to;
 87                 if ( belong[i]!=belong[v] ) {
 88                     in[belong[v]]++;
 89                     out[belong[i]]++;
 90                 }
 91             }
 92         }
 93         ans=0;
 94         cnt=0;
 95         for ( i=1;i<=scc;i++ ) {
 96             if ( !in[i] ) {
 97                 ans+=cost_[i];
 98                 cnt++;
 99             }
100         }
101         printf("%d %d\n",cnt,ans);
102     }
103     return 0;
104 }
HDOJ1827

 

3.(HDOJ2767、HDOJ3836)

http://acm.hdu.edu.cn/showproblem.php?pid=2767                 

http://acm.hdu.edu.cn/showproblem.php?pid=3836

题意:添加多少条可以使得整个图为一个强连通分量。先求出强连通分量的个数,然后需要添加的边为出度和入度为0中较大的那个值(即没有点的入度和出度为0)。需要特别注意的是本身只有一个强连通分量则直接输出0

技术分享图片
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 const int maxn=20050;
  6 const int maxm=50010;
  7 struct edge{
  8     int to,nxt;
  9 }edge[maxm];
 10 int head[maxn],tot;
 11 int low[maxn],dfn[maxn],stack[maxn],belong[maxn];
 12 int index,top;
 13 int scc;
 14 bool vis[maxn];
 15 int num[maxn],in[maxn],out[maxn];
 16 
 17 void addedge(int u,int v)
 18 {
 19     edge[tot].to=v;
 20     edge[tot].nxt=head[u];
 21     head[u]=tot++;
 22 }
 23 
 24 void tarjan(int u)
 25 {
 26     int v;
 27     low[u]=dfn[u]=++index;
 28     stack[top++]=u;
 29     vis[u]=true;
 30     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
 31         v=edge[i].to;
 32         if ( !dfn[v] ) {
 33             tarjan(v);
 34             low[u]=min(low[u],low[v]);
 35         }
 36         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
 37     }    
 38     if ( low[u]==dfn[u] ) {
 39         scc++;
 40         do {
 41             v=stack[--top];
 42             vis[v]=false;
 43             belong[v]=scc;
 44             num[scc]++;
 45         }
 46         while ( v!=u );
 47     }
 48 } 
 49 
 50 void solve(int N)
 51 {
 52     memset(dfn,0,sizeof(dfn));
 53     memset(vis,false,sizeof(vis));
 54     memset(num,0,sizeof(num));
 55     index=scc=top=0;
 56     for ( int i=1;i<=N;i++ ) {
 57         if ( !dfn[i] ) tarjan(i);
 58     }
 59 }
 60 
 61 void init()
 62 {
 63     tot=0;
 64     memset(head,-1,sizeof(head));
 65 }
 66 
 67 int main()
 68 {
 69     int T,i,j,k,n,m,x,y,z,ans,cnt1,cnt2;
 70     scanf("%d",&T);
 71     while ( T-- ) {
 72         init();
 73         scanf("%d%d",&n,&m);
 74         while ( m-- ) {
 75             scanf("%d%d",&x,&y);
 76             addedge(x,y);
 77         }
 78         solve(n);
 79         memset(in,0,sizeof(in));
 80         memset(out,0,sizeof(out));
 81         for ( i=1;i<=n;i++ ) {
 82             for ( j=head[i];j!=-1;j=edge[j].nxt ) {
 83                 int v=edge[j].to;
 84                 if ( belong[v]!=belong[i] ) {
 85                     in[belong[v]]++;
 86                     out[belong[i]]++;
 87                 }
 88             }
 89         }
 90         cnt1=cnt2=0;
 91         for ( i=1;i<=scc;i++ ) {
 92             if ( in[i]==0 ) cnt1++;
 93             if ( out[i]==0 ) cnt2++;
 94         }
 95         ans=max(cnt1,cnt2);
 96         if ( scc==1 ) ans=0;
 97         printf("%d\n",ans);
 98     }
 99     return 0;
100  } 
HDOJ2767

 

4.(HDOJ3639)http://acm.hdu.edu.cn/showproblem.php?pid=3639

题意:有n个小孩要相互投票,有m条两人之间的单向的支持关系,求获得支持最多的人的支持数,并要求把这些人的编号(0~n-1)按升序排列输出来。

分析:首先利用tarjan进行缩点构建新图,首先明确得票数最高的人一定在出度为0的强连通分量团体中,所以当前任务就是确定对于每个出度为0的强连通分量团体有多少人能够直接或间接指向它们。这时候就要在新图上构建反向边,对于出度为0的点i进行搜索,经过的所有点的人数总和(每个点都代表一部分人数)就是支持这个团体i中每个人的总得票数。记录每个出度为0的点的得票数,取最大值。最后对于新图中的每个点当其的得票数为最大值时,其强连通分量团体中的所有点都能够当选。

注意:从样例中可以得到一个人可以同时支持多个人,且支持的人都能得到他的全部票数

技术分享图片
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<vector>
  5 #include<queue>
  6 #include<set>
  7 using namespace std;
  8 const int maxn=20010;
  9 const int maxm=50010;
 10 struct edge{
 11     int to,nxt;
 12 }edge[maxm];
 13 int head[maxn],tot;
 14 int low[maxn],dfn[maxn],stack[maxn],belong[maxn],cnt[maxn];
 15 int index,top;
 16 int scc;
 17 bool vis[maxn],vis_[maxn];
 18 int num[maxn],in[maxn],out[maxn];
 19 vector<int>G[maxn];
 20 
 21 void addedge(int u,int v)
 22 {
 23     edge[tot].to=v;
 24     edge[tot].nxt=head[u];
 25     head[u]=tot++;
 26 }
 27 
 28 void addedge_(int u,int v)
 29 {
 30     G[u].push_back(v);
 31 }
 32 
 33 void tarjan(int u)
 34 {
 35     int v;
 36     low[u]=dfn[u]=++index;
 37     stack[top++]=u;
 38     vis[u]=true;
 39     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
 40         v=edge[i].to;
 41         if ( !dfn[v] ) {
 42             tarjan(v);
 43             low[u]=min(low[u],low[v]);
 44         }
 45         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
 46     }    
 47     if ( low[u]==dfn[u] ) {
 48         scc++;
 49         do {
 50             v=stack[--top];
 51             vis[v]=false;
 52             belong[v]=scc;
 53             num[scc]++;
 54         }
 55         while ( v!=u );
 56     }
 57 } 
 58 
 59 void solve(int N)
 60 {
 61     memset(dfn,0,sizeof(dfn));
 62     memset(vis,false,sizeof(vis));
 63     memset(num,0,sizeof(num));
 64     index=scc=top=0;
 65     for ( int i=1;i<=N;i++ ) {
 66         if ( !dfn[i] ) tarjan(i);
 67     }
 68 }
 69 
 70 void init()
 71 {
 72     tot=0;
 73     memset(head,-1,sizeof(head));
 74 }
 75 
 76 int BFS(int u)
 77 {
 78     memset(vis,false,sizeof(vis));
 79     queue<int>que;
 80     que.push(u);
 81     vis[u]=true;
 82     int now=num[u];
 83     while ( !que.empty() ) {
 84         int v=que.front();
 85         que.pop();
 86         for ( int i=0;i<G[v].size();i++ ) {
 87             int j=G[v][i];
 88             if ( !vis[j] ) {
 89                 vis[j]=true;
 90                 que.push(j);
 91                 now+=num[j];
 92             }
 93         }
 94     }
 95     return now;
 96 }
 97 
 98 int main()
 99 {
100     int T,i,j,k,h,x,y,z,n,m,ans,now,cnt_;
101     scanf("%d",&T);
102     for ( h=1;h<=T;h++ ) {
103         scanf("%d%d",&n,&m);
104         init();
105         for ( i=1;i<=n;i++ ) G[i].clear();
106         for ( i=1;i<=m;i++ ) {
107             scanf("%d%d",&x,&y);
108             x++;y++;
109             addedge(x,y);
110         }
111         solve(n);
112         memset(in,0,sizeof(in));
113         memset(out,0,sizeof(out));
114         memset(cnt,0,sizeof(cnt));
115         memset(vis_,false,sizeof(vis_));
116         for ( i=1;i<=n;i++ ) {
117             for ( j=head[i];j!=-1;j=edge[j].nxt ) {
118                 int v=edge[j].to;
119                 if ( belong[v]!=belong[i] ) {
120                     in[belong[v]]++;
121                     out[belong[i]]++;
122                     addedge_(belong[v],belong[i]);
123                 }
124             }
125         }
126         ans=0;
127         k=0;
128         for ( i=1;i<=scc;i++ ) {
129             if ( out[i]==0 ) {
130                 cnt[i]=BFS(i);
131                 if ( cnt[i]>ans ) ans=cnt[i];
132             }
133         }
134         for ( i=1;i<=scc;i++ ) {
135             if ( cnt[i]==ans ) {
136                 vis_[i]=true;
137                 k+=num[i];
138             }
139         }
140         printf("Case %d: %d\n",h,ans-1);
141         cnt_=0;
142         for ( i=1;i<=n;i++ ) {
143             if ( vis_[belong[i]] ) { 
144                 printf("%d",i-1);
145                 if ( ++cnt_==k ) {
146                     printf("\n");
147                     break;
148                 }
149                 else printf(" ");
150             }
151         }
152     }
153     return 0;
154 }
HDOJ3639

 

5.(HDOJ3072)http://acm.hdu.edu.cn/showproblem.php?pid=3072

题意:

分析:tarjan+类最小生成树

 

6.(HDOJ3861)http://acm.hdu.edu.cn/showproblem.php?pid=3861

题意:有一个国王,有n个城市和m条路径。先将国王分成几个地区,地区满足:若城市u能到城市v同时城市v能到城市u,则它们必定属于一个地区。于此同时还有其他点也可以属于该地区,即一个地区的两点u,v一定存在一条不经过其他地区的从u到v或者从v到u的线路。

分析:tarjan+最小路径覆盖。先用tarjan进行缩点,在新图的基础上构建二分图匹配求出最大匹配进而得到最小路径数。注意点数较多,所有二分匹配用邻接表而不是临界矩阵存。同时点数好像比所给的范围要大的多

技术分享图片
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<vector>
  5 using namespace std;
  6 const int maxn=20010;
  7 const int maxm=100010;
  8 struct edge{
  9     int to,nxt,val;
 10 }edge[maxm];
 11 int head[maxn],tot;
 12 int low[maxn],dfn[maxn],stack[maxn],belong[maxn],girl[maxn];
 13 int index,top;
 14 int scc,n;
 15 bool vis[maxn];
 16 vector<int>G[maxn];
 17 
 18 void addedge(int u,int v)
 19 {
 20     edge[tot].to=v;
 21     edge[tot].nxt=head[u];
 22     head[u]=tot++;
 23 }
 24 
 25 void tarjan(int u)
 26 {
 27     int v;
 28     low[u]=dfn[u]=++index;
 29     stack[top++]=u;
 30     vis[u]=true;
 31     for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
 32         v=edge[i].to;
 33         if ( !dfn[v] ) {
 34             tarjan(v);
 35             low[u]=min(low[u],low[v]);
 36         }
 37         else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
 38     }    
 39     if ( low[u]==dfn[u] ) {
 40         scc++;
 41         do {
 42             v=stack[--top];
 43             vis[v]=false;
 44             belong[v]=scc;
 45         }
 46         while ( v!=u );
 47     }
 48 } 
 49 
 50 void solve()
 51 {
 52     memset(dfn,0,sizeof(dfn));
 53     memset(vis,false,sizeof(vis));
 54     index=scc=top=0;
 55     for ( int i=1;i<=n;i++ ) {
 56         if ( !dfn[i] ) tarjan(i);
 57     }
 58 }
 59 
 60 void init()
 61 {
 62     tot=0;
 63     memset(head,-1,sizeof(head));
 64 }
 65 
 66 bool find(int x)
 67 {
 68     int i,j;
 69     for ( i=0;i<G[x].size();i++ ) {
 70         j=G[x][i];
 71         if (  vis[j]==false ) {
 72             vis[j]=true;
 73             if ( girl[j]==0 || find(girl[j]) ) {
 74                 girl[j]=x;
 75                 return true;
 76             }
 77         }
 78     }
 79     return false;
 80 }
 81 
 82 int main()
 83 {
 84     int m,i,j,k,x,y,z,ans,T;
 85     scanf("%d",&T);
 86     while ( T-- ) {
 87         scanf("%d%d",&n,&m);
 88         init();
 89         while ( m-- ) {
 90             scanf("%d%d",&x,&y);
 91             addedge(x,y);
 92         } 
 93         solve();
 94         for ( i=1;i<=scc;i++ ) G[i].clear();
 95         for ( i=1;i<=n;i++ ) {
 96             for ( j=head[i];j!=-1;j=edge[j].nxt ) {
 97                 int v=edge[j].to;
 98                 x=belong[i];
 99                 y=belong[v];
100                 if ( x!=y )    G[x].push_back(y);    
101             }
102         }
103         ans=0;
104         memset(girl,0,sizeof(girl));
105         for ( i=1;i<=scc;i++ ) {
106             memset(vis,false,sizeof(vis));
107             if ( find(i) ) ans++;
108         }
109         ans=scc-ans;
110         printf("%d\n",ans);
111     }
112     return 0;
113 }
HDOJ3861

 

7.(HDOJ2242)http://acm.hdu.edu.cn/showproblem.php?pid=2242

 

以上是关于专题训练之强连通分量和2-sat的主要内容,如果未能解决你的问题,请参考以下文章

图之强连通强连通图强连通分量 Tarjan算法

图之强连通强连通图强连通分量 Tarjan算法

模板:强连通分量&2-sat

连通分量专题

2-SAT

2-SAT