1. 程式人生 > >【ZSTU4213 2015年12月浙理工校賽 D】【雙連通分量tarjan演算法】One-Way Roads 無向連通圖確定邊的方向使得全圖任意兩點間可達

【ZSTU4213 2015年12月浙理工校賽 D】【雙連通分量tarjan演算法】One-Way Roads 無向連通圖確定邊的方向使得全圖任意兩點間可達

4213: One-Way Roads

Time Limit:1 Sec  Memory Limit:128 MB  Special Judge
Submit:133  Solved:45

Description

In the ACM kingdom, there areNcities connected byMtwo-way roads. These cities are connected, i.e., one can reach from any cityXto any other cityYby going through some of these roads. One day, the government wishes to assign for each road a direction, such that one can still reach from any city to any other. You are asked to determine whether this task is possible.

Input

The first line of input containsT(0 ≤ T ≤ 100), the number of test cases. The first line of each test case consists of two integers,N(1 ≤ N ≤ 50), andM(1 ≤ M ≤ N(N − 1)/2). Each of the nextMlines describes a road, and consists of two integers,XandY,(1 ≤ X, Y ≤ N; X ≠ Y), indicating that there is a road between city

XandY. There is at most one road that directly connects each pair of cities.

Output

For each test case, if it is impossible, output a single lineNO. Otherwise, outputYESon the first line, followed byMlines describing one possible direction assignment to theseMroads. Each of theseMlines should consist of two integers,

X,Y, indicating that there is a one-way road from cityXto cityY. TheseMlines can be output in any order.

Sample Input

3
3 3
1 2
2 3
1 3
4 3
1 2
1 3
1 4
4 5
1 2
2 3
4 3
1 4
2 4

Sample Output

YES
1 2
2 3
3 1
NO
YES
1 2
2 3
3 4
4 1
2 4

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;}
template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;}
const int N=60,M=3600,Z=1e9+7,ms63=0x3f3f3f3f;
int casenum,casei;
int n,m,x,y;
int id,tim,top,color,g;
int first[N],s[N],low[N],dfn[N];bool e[N];
int w[M],nxt[M];pair<int,int>ans[M];
void ins(int x,int y)
{
    ++id;
    w[id]=y;
    nxt[id]=first[x];
    first[x]=id;
}
void tarjan(int x,int lastz)
{
    low[x]=dfn[x]=++tim;
    s[++top]=x;e[x]=1;
    for(int z=first[x];z;z=nxt[z])
    {
		if((z^lastz)==1)continue;
        int y=w[z];
        if(dfn[y]==0)
		{
			ans[++g]=make_pair(x,y);
			tarjan(y,z);
		}
        if(e[y])
		{
			if(dfn[y]<dfn[x])ans[++g]=make_pair(x,y);
			gmin(low[x],low[y]);
		}
    }
    if(dfn[x]==low[x])
    {
        if(++color>1)return;
        while(1)
        {
            int y=s[top--];
            e[y]=0;
            if(y==x)break;
        }
    }
    return;
}
int main()
{
    scanf("%d",&casenum);
    for(casei=1;casei<=casenum;++casei)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
        {
            dfn[i]=low[i]=0;
            first[i]=0;
            e[i]=0;
        }
        id=1;
        for(int i=1;i<=m;++i)
        {
            scanf("%d%d",&x,&y);
            ins(x,y);
            ins(y,x);
        }
		tim=top=color=g=0;
		tarjan(1,0);
        if(color==1)
        {
            puts("YES");
            for(int i=1;i<=g;++i)printf("%d %d\n",ans[i].first,ans[i].second);
        }
		else puts("NO");
    }
    return 0;
}
/*
【題意】
比賽的時候要是有spj,這道題就1A了,這場比賽是大概能夠AK的。唉= = 

給你一個聯通無向圖,讓你使得每一條邊有向化,
問你可否在這個基礎上,使得圖上的任意兩點間都存在一條可達路徑。

【型別】
雙連通分量

【分析】
這題只要判定圖中是否存在一個極大(包含圖上所有點)雙連通分量即可。
於是我們直接套上可以解決雙連通分量的tarjan演算法。

從父節點連向子節點的邊,我們不再次處理(否則就出錯了),
其他所有邊的方向,只要順著dfs的方向連就好啦!
然後看看是否雙連通分量只有一個。如果是,輸出YES和具體的連邊方案就好啦。

注意,一條邊只能連一次。
我們是可能從一個時間戳較大的點,走到一個時間戳較小的點的。
而這條邊也恰恰需要從這個時間戳較大的點,指向時間戳較小的點。
然而,這是深搜,我們可能後來還會走回那個時間戳較小的點,這時要注意,不要再把邊的連向搞反了。

換句話說,我們連邊的原則是——
1,連向一個新的點時,方向是指向新的點的。
2,放棄返祖邊。
3,兩個點都不是新點時,方向是指向舊的點(時間戳小的點)。

【時間複雜度&&優化】
O(n)

*/