欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

连通图练习总结

程序员文章站 2022-05-21 16:14:07
...

连通图是图论基于联通的一个概念,在ACM中针对图论的考察一部分是也是基于连通图。针对这类问题的解题基本思路就是先求出对应的连通分量(有向图的强连通,无向图的双连通)对图进行简化,然后再结合其他算法计算。

1. POJ 3180 The Cow Prom

这个题如果能理解题目的话,怎么做就很明显了,能形成一个可以转圈的小群,就相当于一个强连通分量,需要注意的就是这个小群不可以只有一头牛。

#include <set>
#include <map>
#include <list>
#include <stack>
#include <queue>
#include <ctime>
#include <cmath>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

#pragma comment(linker, "/STACK:1024000000,1024000000")

#define     IT              iterator
#define     PB(x)           push_back(x)
#define     CLR(a,b)        memset(a,b,sizeof(a))

using namespace std;

typedef     long long               ll;
typedef     unsigned long long      ull;
typedef     vector<int>             vint;
typedef     vector<ll>              vll;
typedef     vector<ull>             vull;
typedef     set<int>                sint;
typedef     set<ull>                sull;

const int maxn = 10000 + 5;
vint G[maxn];
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
stack<int> S;
int a[maxn];

void init(int n) {
    for (int i = 0; i <= n; i++) G[i].clear();
}

void dfs(int u) {
    pre[u] = lowlink[u] = ++dfs_clock;
    S.push(u);
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (!pre[v]) {
            dfs(v);
            lowlink[u] = min(lowlink[u],lowlink[v]);
        }
        else if (!sccno[v]) {
            lowlink[u] = min(lowlink[u],pre[v]);
        }
    }
    if (lowlink[u] == pre[u]) {
        scc_cnt++;
        while (1) {
            int x = S.top();
            S.pop();
            sccno[x] = scc_cnt;
            if (x == u) break;
        }
    }
}

void find_scc(int n) {
    dfs_clock = scc_cnt = 0;
    CLR(sccno,0);
    CLR(pre,0);
    for (int i = 1; i <= n; i++) {
        if (!pre[i]) dfs(i);
    }
}

int cnt[maxn];

int main() {
    int n,m;
    //freopen("data.in","r",stdin);
    while (cin>>n>>m) {
        init(n);
        for (int i = 0; i < m; i++) {
            int a,b;
            scanf("%d%d",&a,&b);
            G[a].PB(b);
        }
        //for (int i = 0; i <= scc_cnt; i++) new_g[i].clear();
        find_scc(n);
        for (int i = 1; i <= n; i++) {
            cnt[sccno[i]]++;
        }
        int ans = 0;
        /*cout<<scc_cnt<<endl;
        for (int i = 1; i <= scc_cnt; i++) {
            for (int j = 0; j < new_g[i].size(); j++) {
                cout<<i<<" "<<new_g[i][j].from<<" "<<new_g[i][j].cost<<endl;
            }
        }*/
        for (int i = 1; i <= scc_cnt; i++) {
            if (cnt[i] > 1) ans++;
        }
        cout<<ans<<endl;
    }
}

 2. POJ 1236 Network of Schools

最开始对于图的处理同样也是需要用强连通缩点重新构图,然后针对两个任务,第一个很好理解就是入度为0的强连通个数,第二个问题需要小小想一下,欲将整个图变成一个强连通,那么就相当于把现在的图中入度为0和出度为0的点都消灭掉(加边),那么想到这里答案就出来了,就是max{入度为0的点个数,出度为0的点个数}。

#include <set>
#include <map>
#include <list>
#include <stack>
#include <queue>
#include <ctime>
#include <cmath>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

#pragma comment(linker, "/STACK:1024000000,1024000000")

#define     IT              iterator
#define     PB(x)           push_back(x)
#define     CLR(a,b)        memset(a,b,sizeof(a))

using namespace std;

typedef     long long               ll;
typedef     unsigned long long      ull;
typedef     vector<int>             vint;
typedef     vector<ll>              vll;
typedef     vector<ull>             vull;
typedef     set<int>                sint;
typedef     set<ull>                sull;

const int maxn = 10000 + 5;
vint G[maxn];
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
stack<int> S;
int a[maxn];

void init(int n) {
    for (int i = 0; i <= n; i++) G[i].clear();
}

void dfs(int u) {
    pre[u] = lowlink[u] = ++dfs_clock;
    S.push(u);
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (!pre[v]) {
            dfs(v);
            lowlink[u] = min(lowlink[u],lowlink[v]);
        }
        else if (!sccno[v]) {
            lowlink[u] = min(lowlink[u],pre[v]);
        }
    }
    if (lowlink[u] == pre[u]) {
        scc_cnt++;
        while (1) {
            int x = S.top();
            S.pop();
            sccno[x] = scc_cnt;
            if (x == u) break;
        }
    }
}

void find_scc(int n) {
    dfs_clock = scc_cnt = 0;
    CLR(sccno,0);
    CLR(pre,0);
    for (int i = 1; i <= n; i++) {
        if (!pre[i]) dfs(i);
    }
}

int in[maxn];
int out[maxn];

int main() {
    int n,m;
    //freopen("data.in","r",stdin);
    while (cin>>n) {
        init(n);
        for (int i = 1; i <= n; i++) {
            int m;
            while (scanf("%d",&m),m) {
                G[i].PB(m);
            }
        }
        //for (int i = 0; i <= scc_cnt; i++) new_g[i].clear();
        find_scc(n);
        CLR(in,0);
        CLR(out,0);
        int tmpa = 0,tmpb = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < G[i].size(); j++) {
                int v = G[i][j];
                if (sccno[i] != sccno[v]) {
                    out[sccno[i]]++;
                    in[sccno[v]]++;
                }
            }
        }
        for (int i = 1; i <= scc_cnt; i++) {
            if (!out[i]) tmpb++;
            if (!in[i]) tmpa++;
        }
        cout<<tmpa<<endl<<(scc_cnt == 1 ? 0 : max(tmpb,tmpa))<<endl;
    }
}

 3. POJ 2186 Popular Cows

题目给出一些仰慕关系,需要求的是被所有牛仰慕的牛的个数。由于图相对比较大,所以先强连通缩点,重新构图之后,这样之后就相对简单了,保证图连通的情况下,我们需要保证出度为0的分量只有一个,如果有多个,那么这几个分量之间必然没有倾慕关系。

#include <set>
#include <map>
#include <list>
#include <stack>
#include <queue>
#include <ctime>
#include <cmath>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

#pragma comment(linker, "/STACK:1024000000,1024000000")

#define     IT              iterator
#define     PB(x)           push_back(x)
#define     CLR(a,b)        memset(a,b,sizeof(a))

using namespace std;

typedef     long long               ll;
typedef     unsigned long long      ull;
typedef     vector<int>             vint;
typedef     vector<ll>              vll;
typedef     vector<ull>             vull;
typedef     set<int>                sint;
typedef     set<ull>                sull;

const int maxn = 10000 + 5;
vint G[maxn];
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
stack<int> S;
int a[maxn];

void init(int n) {
    for (int i = 0; i <= n; i++) G[i].clear();
}

void dfs(int u) {
    pre[u] = lowlink[u] = ++dfs_clock;
    S.push(u);
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (!pre[v]) {
            dfs(v);
            lowlink[u] = min(lowlink[u],lowlink[v]);
        }
        else if (!sccno[v]) {
            lowlink[u] = min(lowlink[u],pre[v]);
        }
    }
    if (lowlink[u] == pre[u]) {
        scc_cnt++;
        while (1) {
            int x = S.top();
            S.pop();
            sccno[x] = scc_cnt;
            if (x == u) break;
        }
    }
}

void find_scc(int n) {
    dfs_clock = scc_cnt = 0;
    CLR(sccno,0);
    CLR(pre,0);
    for (int i = 1; i <= n; i++) {
        if (!pre[i]) dfs(i);
    }
}

int out[maxn];
int cnt[maxn];

int main() {
    int n,m;
    //freopen("data.in","r",stdin);
    while (cin>>n>>m) {
        init(n);
        for (int i = 0; i < m; i++) {
            int a,b;
            scanf("%d%d",&a,&b);
            G[a].PB(b);
        }
        //for (int i = 0; i <= scc_cnt; i++) new_g[i].clear();
        find_scc(n);
        CLR(out,0);
        CLR(cnt,0);
        for (int i = 1; i <= n; i++) {
            cnt[sccno[i]]++;
            for (int j = 0; j < G[i].size(); j++) {
                int v = G[i][j];
                if (sccno[i] != sccno[v]) {
                    out[sccno[i]]++;
                }
            }
        }
        bool flag = true;
        int k = 0;
        for (int i = 1; i <= scc_cnt; i++) {
            if (k && !out[i]) {
                flag = false;
                break;
            }
            if (!out[i]) k = i;
        }
        if (flag) cout<<cnt[k]<<endl;
        else cout<<0<<endl;
    }
}

 4. HDU 3861 The King’s Problem

题目读完之后发现应该是最小路径覆盖,但是两个问题:数据范围和图有环。先强连通缩点,重新构图,这样图就变成了DAG,在应用Hungary算法就可以求出答案了。

#include <set>
#include <map>
#include <list>
#include <stack>
#include <queue>
#include <ctime>
#include <cmath>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

#pragma comment(linker, "/STACK:1024000000,1024000000")

#define     IT              iterator
#define     PB(x)           push_back(x)
#define     CLR(a,b)        memset(a,b,sizeof(a))

using namespace std;

typedef     long long               ll;
typedef     unsigned long long      ull;
typedef     vector<int>             vint;
typedef     vector<ll>              vll;
typedef     vector<ull>             vull;
typedef     set<int>                sint;
typedef     set<ull>                sull;

const int maxn = 50000 + 5;
vint G[maxn];
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
stack<int> S;
int a[maxn];

void init(int n) {
    for (int i = 0; i <= n; i++) G[i].clear();
}

void dfs(int u) {
    pre[u] = lowlink[u] = ++dfs_clock;
    S.push(u);
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (!pre[v]) {
            dfs(v);
            lowlink[u] = min(lowlink[u],lowlink[v]);
        }
        else if (!sccno[v]) {
            lowlink[u] = min(lowlink[u],pre[v]);
        }
    }
    if (lowlink[u] == pre[u]) {
        scc_cnt++;
        while (1) {
            int x = S.top();
            S.pop();
            sccno[x] = scc_cnt;
            if (x == u) break;
        }
    }
}

void find_scc(int n) {
    dfs_clock = scc_cnt = 0;
    CLR(sccno,0);
    CLR(pre,0);
    for (int i = 1; i <= n; i++) {
        if (!pre[i]) dfs(i);
    }
}

vint new_g[maxn];
int match[maxn];
bool vis[maxn];

bool findpath(int u) {
    for (int i = 0; i < new_g[u].size(); i++) {
        int v = new_g[u][i];
        if (!vis[v]) {
            vis[v] = 1;
            if (match[v] == -1 || findpath(match[v])) {
                match[v] = u;
                return true;
            }
        }
    }
    return false;
}

int hungary() {
    int ans = 0;
    CLR(match,-1);
    for (int i = 1; i <= scc_cnt; i++) {
        CLR(vis,0);
        if (findpath(i)) ans++;
    }
    return ans;
}

int main() {
    //freopen("data.in","r",stdin);
    int n,m;
    int T;
    cin>>T;
    while (T--) {
        cin>>n>>m;
        init(n);
        for (int i = 0; i < m; i++) {
            int a,b;
            scanf("%d%d",&a,&b);
            G[a].PB(b);
        }
        //for (int i = 0; i <= scc_cnt; i++) new_g[i].clear();
        find_scc(n);
        for (int i = 0; i <= scc_cnt; i++) new_g[i].clear();
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < G[i].size(); j++) {
                int v = G[i][j];
                if (sccno[i] != sccno[v]) {
                    new_g[sccno[i]].PB(sccno[v]);
                }
            }
        }
        cout<<scc_cnt - hungary()<<endl;
    }
}

 5. HDU 3639 Hawk-and-Chicken

开始还是老套路,先强连通缩点,需要注意的话这个题重新构图需要反向建边,然后对每个入度0的点DFS,求最大值即可。

#include <set>
#include <map>
#include <list>
#include <stack>
#include <queue>
#include <ctime>
#include <cmath>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

#pragma comment(linker, "/STACK:1024000000,1024000000")

#define     IT              iterator
#define     PB(x)           push_back(x)
#define     CLR(a,b)        memset(a,b,sizeof(a))

using namespace std;

typedef     long long               ll;
typedef     unsigned long long      ull;
typedef     vector<int>             vint;
typedef     vector<ll>              vll;
typedef     vector<ull>             vull;
typedef     set<int>                sint;
typedef     set<ull>                sull;

const int maxn = 5000 + 5;
vint G[maxn];
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
stack<int> S;
int a[maxn];

void init(int n) {
    for (int i = 0; i <= n; i++) G[i].clear();
}

void dfs(int u) {
    pre[u] = lowlink[u] = ++dfs_clock;
    S.push(u);
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (!pre[v]) {
            dfs(v);
            lowlink[u] = min(lowlink[u],lowlink[v]);
        }
        else if (!sccno[v]) {
            lowlink[u] = min(lowlink[u],pre[v]);
        }
    }
    if (lowlink[u] == pre[u]) {
        scc_cnt++;
        while (1) {
            int x = S.top();
            S.pop();
            sccno[x] = scc_cnt;
            if (x == u) break;
        }
    }
}

void find_scc(int n) {
    dfs_clock = scc_cnt = 0;
    CLR(sccno,0);
    CLR(pre,0);
    for (int i = 0; i < n; i++) {
        if (!pre[i]) dfs(i);
    }
}

vint new_g[maxn];
int rec[maxn];
int out[maxn];
int lab[maxn];
bool vis[maxn];
int cnt;

int dp(int u) {
    vis[u] = true;
    cnt += lab[u];
    for (int i = 0; i < new_g[u].size(); i++) {
        int v = new_g[u][i];
        if (!vis[v]) {
            dp(v);
        }
    }
    return cnt;
}

int main() {
    //freopen("data.in","r",stdin);
    int n,m;
    int T;
    cin>>T;
    for (int t = 1; t <= T; t++) {
        cin>>n>>m;
        init(n);
        for (int i = 0; i < m; i++) {
            int a,b;
            scanf("%d%d",&a,&b);
            G[a].PB(b);
        }
        find_scc(n);
        CLR(out,0);
        CLR(lab,0);
        for (int i = 0; i <= scc_cnt; i++) new_g[i].clear();
        for (int i = 0; i < n; i++) {
            lab[sccno[i]]++;
            for (int j = 0; j < G[i].size(); j++) {
                int v = G[i][j];
                if (sccno[i] != sccno[v]) {
                    new_g[sccno[v]].PB(sccno[i]);
                    out[sccno[i]]++;
                }
            }
        }
        CLR(rec,0);
        int ans = 0;
        for (int i = 1; i <= scc_cnt; i++) {
            if (!out[i]) {
                CLR(vis,0);
                cnt = 0;
                rec[i] = dp(i);
                ans = max(ans,rec[i]);
            }
        }
        /*for (int i = 0; i < n; i++) {
            cout<<sccno[i]<<" ";
        }
        cout<<endl;
        for (int i = 1; i <= scc_cnt; i++) {
            cout<<rec[i][0]<<" "<<rec[i][1]<<endl;
        }*/
        printf("Case %d: %d\n",t,ans - 1);
        bool flag = false;
        for (int i = 0; i < n; i++) {
            if (rec[sccno[i]] == ans) {
                if (flag) printf(" %d",i);
                else {
                    printf("%d",i);
                    flag = true;
                }
            }
        }
        printf("\n");
    }
}

 6. HDU 2242 考研路茫茫――空调教室

题目很好理解,给出了无向图,断开一条边使得分成的两个部分人数差最小。开始先用tarjan求一下边双联通,确定这个图当中桥有哪些,答案就是这些桥中的一个。然后对于缩完点的图,应该是一棵树,这样的话就可以利用DFS的性质,对这一棵树DFS一遍,就可以求出人数差了。

#include <set>
#include <map>
#include <list>
#include <stack>
#include <queue>
#include <ctime>
#include <cmath>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

#pragma comment(linker, "/STACK:1024000000,1024000000")

#define     IT              iterator
#define     PB(x)           push_back(x)
#define     CLR(a,b)        memset(a,b,sizeof(a))

using namespace std;

typedef     long long               ll;
typedef     unsigned long long      ull;
typedef     vector<int>             vint;
typedef     vector<ll>              vll;
typedef     vector<ull>             vull;
typedef     set<int>                sint;
typedef     set<ull>                sull;

const int maxn = 20000 + 5;
struct Edge {
    int to,next;
    bool cut;
    bool cong;
};
stack<int> S;
const int maxm = 40000 + 5;
Edge edge[maxm];
int head[maxn],tot;
int low[maxn],dfn[maxn],belong[maxn];
int Index;
int block;
bool Instack[maxn];
int bridge;
int peo[maxn];
int tpe[maxn];

void addedge(int u,int v) {
    edge[tot].to = v;
    edge[tot].next = head[u];
    edge[tot].cut = false;
    edge[tot].cong = false;
    head[u] = tot++;
}

void tarjan(int u,int pre,bool ff) {
    int v;
    low[u] = dfn[u] = ++Index;
    S.push(u);
    Instack[u] = true;
    for (int i = head[u]; i != -1; i = edge[i].next) {
        v = edge[i].to;
        if (v == pre && (!ff)) continue;
        if (!dfn[v]) {
            tarjan(v,u,edge[i].cong);
            if (low[u] > low[v]) low[u] = low[v];
            if (low[v] > dfn[u]) {
                bridge++;
                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 = S.top();
            S.pop();
            Instack[v] = false;
            belong[v] = block;
        }while (v != u);
    }
}

void init() {
    tot = 0;
    CLR(head,-1);
    while (!S.empty()) S.pop();
    CLR(tpe,0);
}

int sum = 0;
map<ll,int> edgelab;
vint g[maxn];
int minans;

int dfs(int u,int pre) {
    int ans = tpe[u];
    for (int i = 0; i < g[u].size(); i++) {
        if (g[u][i] == pre) continue;
        ans += dfs(g[u][i],u);
    }
    //cout<<ans<<endl;
    minans = min(minans,abs(sum - 2 * ans));
    return ans;
}

int main() {
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    int n,m;
    while (cin>>n>>m) {
        init();
        sum = 0;
        edgelab.clear();
        for (int i = 1; i <= n; i++) {
            scanf("%d",peo+i);
            sum += peo[i];
        }
        for (int i = 0; i < m; i++) {
            int a,b;
            scanf("%d%d",&a,&b);
            a++;b++;
            if (a < b) swap(a,b);
            ll tmp = a * 100000 + b;
            if (edgelab.find(tmp) != edgelab.end()) {
                int lab = edgelab[tmp];
                edge[lab].cong = true;
                edge[lab^1].cong = true;
            }
            else {
                edgelab[tmp] = tot;
                addedge(a,b);
                addedge(b,a);
            }
        }
        CLR(dfn,0);
        CLR(Instack,false);
        Index = block = 0;
        tarjan(1,0,false);
        if (block == 1) {
            cout<<"impossible"<<endl;
            continue;
        }
        for (int i = 0; i <= block; i++) g[i].clear();
        minans = 0x3f3f3f3f;
        for (int i = 1; i <= n; i++) {
            for (int j = head[i]; j != -1; j = edge[j].next) {
                if (edge[j].cut) {
                    g[belong[i]].PB(belong[edge[j].to]);
                }
            }
        }
        //for (int i = 1; i <= n; i++) cout<<belong[i]<<endl;
        for (int i = 1; i <= n; i++) tpe[belong[i]] += peo[i];
        dfs(1,0);
        cout<<minans<<endl;
    }
}

 7. HDU 3849 By Recognizing These Guys, We Find Social Networks Useful

先对顶点进行哈希,然后找到图中的桥,输出出来就行了。需要注意的是,如果图不是连通图的,是不存在桥的概念的,所以缩点之后需要判一下图连通性。

#include <set>
#include <map>
#include <list>
#include <stack>
#include <queue>
#include <ctime>
#include <cmath>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

#pragma comment(linker, "/STACK:1024000000,1024000000")

#define     IT              iterator
#define     PB(x)           push_back(x)
#define     CLR(a,b)        memset(a,b,sizeof(a))

using namespace std;

typedef     long long               ll;
typedef     unsigned long long      ull;
typedef     vector<int>             vint;
typedef     vector<ll>              vll;
typedef     vector<ull>             vull;
typedef     set<int>                sint;
typedef     set<ull>                sull;

struct Edge {
    int to,next;
    bool cut;//是否是桥
    bool cong;//判断重边
};

const int maxn = 20000 + 5;
const int maxm = 200000 + 5;
Edge edge[maxm];
int head[maxn],tot;
int low[maxn],dfn[maxn],Stack[maxn],belong[maxn];
int Index,top;
int block;
bool Instack[maxn];
int bridge;
int n,m;
map<string,int> has;
map<int,string> fas;

void addedge(int u,int v,int tt) {
    edge[tot].to = v;
    edge[tot].next = head[u];
    edge[tot].cut = false;
    edge[tot].cong = tt;
    head[u] = tot++;
}

void tarjan(int u,int pre) {
    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,u);
            if (low[u] > low[v]) low[u] = low[v];
            if (low[v] > dfn[u]) {
                bridge++;
                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;
        } while (v != u);
    }
}

int fa[maxn];

void init() {
    tot = 0;
    for (int i = 0; i < maxn; i++) fa[i] = i;
    has.clear();
    fas.clear();
    CLR(head,-1);
}

int getf(int x) {
    if (fa[x] == x) return x;
    else return fa[x] = getf(fa[x]);
}

void unit(int x,int y) {
    x = getf(x);
    y = getf(y);
    fa[x] = y;
}

int main() {
    int T;
    cin>>T;
    while (T--) {
        scanf("%d%d",&n,&m);
        init();
        int cnt = 1;
        for (int i = 0; i < m; i++) {
            char s1[20],s2[20];
            scanf("%s%s",s1,s2);
            string tmpa(s1);
            string tmpb(s2);
            int a,b;
            if (has.count(tmpa)) a = has[tmpa];
            else {
                has[tmpa] = a = cnt++;
                fas[a] = tmpa;
            }
            if (has.count(tmpb)) b = has[tmpb];
            else {
                has[tmpb] = b = cnt++;
                fas[b] = tmpb;
            }
            unit(a,b);
            addedge(a,b,true);
            addedge(b,a,false);
        }
        CLR(dfn,0);
        CLR(Instack,false);
        Index = top = block = 0;
        tarjan(1,0);
        int tmp = 0;
        for (int i = 1; i <= n; i++) {
            if (fa[i] == i) tmp++;
        }
        if (tmp == 1) {
            printf("%d\n",block-1);
            for (int i = 1; i <= n; i++) {
                for (int j = head[i]; j != -1; j = edge[j].next) {
                    if (edge[j].cut && edge[j].cong) {
                        printf("%s %s\n",fas[i].c_str(),fas[edge[j].to].c_str());
                    }
                }
            }
        }
        else printf("0\n");
    }
}