题意:给出一个连通图,边是双向边,要求令尽可能多的双向边改成单向边,并且图还是连通的(强连通)。
思路:这题其实不太难搞。我们可以想一下,那些边是一定不能改造的?没错,是桥,如果桥被改成单向边,那么就不可能强连通,另外,对于一个双连通分量来说,一定可以把它们的边改成单向并且还是连通的。这样,算法就比较明确了,对于桥来说,不能改造,对于一个双连通分量来说,改造它的所有边。如何改造双连通分量中的边?其实仔细想一下就可以发现,tarjan的过程中的遍历顺序恰好是一个可行的方案,因此,可以再tarjan的过程中直接把结果输出,用一个数组标记一条边是否被改造即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=1000+10;
const int maxm=10000+10;
struct Edge
{
int v,next;
Edge(){};
Edge(int vv,int nx){v=vv;next=nx;}
}edges[maxm<<1];
int head[maxn],nEdge;
int pre[maxn],id[maxn],ebc_cnt,dfs_clock;
bool flag[maxm<<1];
stack<int>S;
void AddEdge(int u,int v)
{
edges[++nEdge]=Edge(v,head[u]);
head[u]=nEdge;
edges[++nEdge]=Edge(u,head[v]);
head[v]=nEdge;
}
int tarjan(int u,int fa)
{
int lowu=pre[u]=++dfs_clock;
S.push(u);
for(int k=head[u];k!=-1;k=edges[k].next)
{
int v=edges[k].v;
if(!pre[v])
{
int lowv=tarjan(v,u);
lowu=min(lowu,lowv);
if(lowv>pre[u])
{
printf("%d %d\n",u,v);
printf("%d %d\n",v,u);
flag[k]=flag[k^1]=true;
ebc_cnt++;
while(true)
{
int x=S.top();S.pop();
id[x]=ebc_cnt;
if(x==v) break;
}
}
}
else if(pre[v]<lowu&&v!=fa)
lowu=min(lowu,pre[v]);
if(!flag[k]&&v!=fa)
{
flag[k]=flag[k^1]=true;
printf("%d %d\n",u,v);
}
}
return lowu;
}
void find_ebc(int n)
{
memset(pre,0,sizeof(pre));
memset(flag,0,sizeof(flag));
memset(id,0,sizeof(id));
while(!S.empty()) S.pop();
dfs_clock=ebc_cnt=0;
for(int i=1;i<=n;++i)
if(!pre[i]) tarjan(i,-1);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n,m;
int tcase=0;
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0) break;
printf("%d\n\n",++tcase);
int u,v;
memset(head,0xff,sizeof(head));
nEdge=-1;
for(int i=0;i<m;++i)
{
scanf("%d%d",&u,&v);
AddEdge(u,v);
}
find_ebc(n);
printf("#\n");
}
return 0;
}