专题训练之强连通分量

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

题意:有n个人编号从0到n-1,给出m组关系<u,v,w>表示u联系v需要费用w(但不代表v联系u需要费用w)。若一个集合中 任意两个人可以互相联系(不管是直接联系的还是通过其他人间接联系的),那么在这个集合里面联系的费用可以忽略。现在你是编号0,问你联系到所有人的最小费用。题目保证至少有一组方案使得你可以联系到所有人。

分析:tarjan+贪心。利用tarjan进行缩点后得到新图,然后需要求出新图的带权最小生成树。因为每个点必定会到达一次,所有对于每个点考虑边权最小的一条入边即可(因为最后这个图必定是联通的)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=50010;
const int maxm=100010;
const int inf=1e9;
struct edge{
    int to,nxt,val;
}edge[maxm];
int head[maxn],tot;
int low[maxn],dfn[maxn],stack[maxn],belong[maxn];
int index,top;
int scc;
bool vis[maxn];
int num[maxn],dis[maxn];

void addedge(int u,int v,int val)
{
    edge[tot].to=v;
    edge[tot].nxt=head[u];
    edge[tot].val=val;
    head[u]=tot++;
}

void tarjan(int u)
{
    int v;
    low[u]=dfn[u]=++index;
    stack[top++]=u;
    vis[u]=true;
    for ( int i=head[u];i!=-1;i=edge[i].nxt ) {
        v=edge[i].to;
        if ( !dfn[v] ) {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if ( vis[v] ) low[u]=min(low[u],dfn[v]);
    }    
    if ( low[u]==dfn[u] ) {
        scc++;
        do {
            v=stack[--top];
            vis[v]=false;
            belong[v]=scc;
            num[scc]++;
        }
        while ( v!=u );
    }
} 

void solve(int N)
{
    memset(dfn,0,sizeof(dfn));
    memset(vis,false,sizeof(vis));
    memset(num,0,sizeof(num));
    index=scc=top=0;
    for ( int i=1;i<=N;i++ ) {
        if ( !dfn[i] ) tarjan(i);
    }
}

void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
}

int main()
{
    int n,m,i,j,k,x,y,z,ans;
    while ( scanf("%d%d",&n,&m)!=EOF ) {
        init();
        while ( m-- ) {
            scanf("%d%d%d",&x,&y,&z);
            x++;y++;
            addedge(x,y,z);
        }
        solve(n);
        for ( i=1;i<=scc;i++ ) dis[i]=inf;
        for ( i=1;i<=n;i++ ) {
            for ( j=head[i];j!=-1;j=edge[j].nxt ) {
                int v=edge[j].to;
                x=belong[i];
                y=belong[v];
                if ( x!=y ) dis[y]=min(dis[y],edge[j].val);
            }
        }
        ans=0;
        dis[belong[1]]=0;
        for ( i=1;i<=scc;i++ ) ans+=dis[i];
        printf("%d\n",ans);
    }
    return 0;
}
HDOJ3072

 

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.(POJ1904)http://poj.org/problem?id=1904

题意:一个国王有n个王子,同时有n个女孩。每个王子都有自己喜欢的若干个女孩,现给定一个合法的完备匹配(也就是一个王子娶其中一个自己喜欢女孩),求每个王子可以选择哪些女孩可以让剩下的每个王子依旧能够选择到自己喜欢的一个女孩。

分析:给王子标号1-n,女孩标号n+1-2*n,王子喜欢女孩则连一条从王子到女孩的边,给定的合法匹配中连一条女孩到王子的边。对于某个王子看与他连边的女孩中是否和他属于同一个强连通分量。

推荐一个较为详细的分析http://www.cnblogs.com/zxndgv/archive/2011/08/06/2129333.html

注意:n,m的范围看清楚,不然很容易RE。最终的答案一定是按升序输出的。在一个强连通分量中可能存在王子不喜欢某个女孩的情况,这时候我们采用的方法是对于每个点(只考虑代表王子的点),遍历他全部的边看边的两端是否属于同一个强连通分量(因为刚开始建的边一定满足男孩喜欢女孩),属于就保存下来。后排序输出

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<vector>
  5 using namespace std;
  6 const int maxn=5010;
  7 const int maxm=250010;
  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],ans[maxn];
 13 int index,top;
 14 int scc,n;
 15 bool vis[maxn];
 16 int num[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             num[scc]++;
 46         }
 47         while ( v!=u );
 48     }
 49 }
 50 
 51 void solve()
 52 {
 53     memset(dfn,0,sizeof(dfn));
 54     memset(vis,false,sizeof(vis));
 55     memset(num,0,sizeof(num));
 56     index=scc=top=0;
 57     for ( int i=1;i<=2*n;i++ ) {
 58         if ( !dfn[i] ) tarjan(i);
 59     }
 60     for ( int i=1;i<=n;i++ ) {
 61         int cnt=0;
 62         for ( int j=head[i];j!=-1;j=edge[j].nxt ) {
 63             int v=edge[j].to;
 64             int x=belong[i];
 65             int y=belong[v];
 66             if ( x==y  ) ans[cnt++]=v-n;
 67         }
 68         sort(ans,ans+cnt);
 69         printf("%d",cnt);
 70         for ( int k=0;k<cnt;k++ ) printf(" %d",ans[k]);
 71         printf("\n");
 72     }
 73 }
 74 
 75 void init()
 76 {
 77     tot=0;
 78     memset(head,-1,sizeof(head));
 79 }
 80 
 81 int main()
 82 {
 83     int m,i,j,k,x,y,z;
 84     while ( scanf("%d",&n)!=EOF ) {
 85         init();
 86         for ( i=1;i<=n;i++ ) {
 87             scanf("%d",&m);
 88             for ( j=1;j<=m;j++ ) {
 89                 scanf("%d",&x);
 90                 addedge(i,x+n);
 91             }
 92         }
 93         for ( i=1;i<=n;i++ ) {
 94             scanf("%d",&x);
 95             addedge(x+n,i);
 96         }
 97         solve();
 98     }
 99     return 0;
100 }
POJ1904

 

8.(POJ2186)http://poj.org/problem?id=2186

题意:有n头牛,m条关系(A,B),即A认为B很受欢迎。先求是有多少人受到其他所有人的欢迎

分析:利用tarjan进行缩点,对于最受欢迎的人必定是出度为0的点,然后在利用反向图+bfs判断该点是否能到达其他所有点

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

 

10.(POJ2553)http://poj.org/problem?id=2553

题意:给定一个有向图,若v为源点,那么如果v能够到达的点u,则u也一定可以到达点v。

分析:满足条件的源点一定是处于出度为0的强连通分量中

  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],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 n,m,i,j,k,x,y,z,cnt,now;
 70     while ( scanf("%d",&n)!=EOF && n ) {
 71         scanf("%d",&m);
 72         init();
 73         while ( m-- ) {
 74             scanf("%d%d",&x,&y);
 75             addedge(x,y);
 76         }
 77         solve(n);
 78         memset(vis,false,sizeof(vis));
 79         memset(out,0,sizeof(out));
 80         for ( i=1;i<=n;i++ ) {
 81             for ( j=head[i];j!=-1;j=edge[j].nxt ) {
 82                 int v=edge[j].to;
 83                 x=belong[i];
 84                 y=belong[v];
 85                 if ( x!=y ) out[x]++;
 86             }
 87         }
 88         cnt=0;
 89         for ( i=1;i<=scc;i++ ) {
 90             if ( out[i]==0 ) {
 91                 vis[i]=true;
 92                 cnt+=num[i];
 93             }
 94         }
 95         if ( cnt==0 ) {
 96             printf("\n");
 97             continue;
 98         }
 99         now=0;
100         for ( i=1;i<=n;i++ ) {
101             if ( vis[belong[i]] ) {
102                 printf("%d",i);
103                 if ( ++now!=cnt ) printf(" ");
104                 else printf("\n");
105             }
106         }
107     }
108     return 0;
109 }
POJ2553

 

11.(POJ3114)http://poj.org/problem?id=3114

题意:给出n个城市,m条送信的渠道,u v w代表u城市送信到v城市需要w小时。同时如果两个城市属于一个国家,那么送信时间为0,在一个国家中的条件是所有城市相互之间可以送信到达。

分析:tarjan+spfa。首先利用tarjan进行缩点,因为可能存在重边所以距离存在数组中。缩点后构建新图。对于每个询问分别求一次spfa。

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

 

posted @ 2018-04-09 21:41  HDU_jackyan  阅读(1141)  评论(0编辑  收藏  举报