Street Directions(POJ 1515)---无向连通图->有向强连通图

题目链接

题目描述

According to the Automobile Collision Monitor (ACM), most fatal traffic accidents occur on two-way streets. In order to reduce the number of fatalities caused by traffic accidents, the mayor wants to convert as many streets as possible into one-way streets. You have been hired to perform this conversion, so that from each intersection, it is possible for a motorist to drive to all the other intersections following some route.
You will be given a list of streets (all two-way) of the city. Each street connects two intersections, and does not go through an intersection. At most four streets meet at each intersection, and there is at most one street connecting any pair of intersections. It is possible for an intersection to be the end point of only one street. You may assume that it is possible for a motorist to drive from each destination to any other destination when every street is a two-way street.

输入格式

The input consists of a number of cases. The first line of each case contains two integers n and m. The number of intersections is n (2 <= n <= 1000), and the number of streets is m. The next m lines contain the intersections incident to each of the m streets. The intersections are numbered from 1 to n, and each street is listed once. If the pair i j is present, j i will not be present. End of input is indicated by n = m = 0.

输出格式

For each case, print the case number (starting from 1) followed by a blank line. Next, print on separate lines each street as the pair i j to indicate that the street has been assigned the direction going from intersection i to intersection j. For a street that cannot be converted into a one-way street, print both i j and j i on two different lines. The list of streets can be printed in any order. Terminate each case with a line containing a single `#’ character.
Note: There may be many possible direction assignments satisfying the requirements. Any such assignment is acceptable.

输入样例

7 10
1 2
1 3
2 4
3 4
4 5
4 6
5 7
6 7
2 5
3 6
7 9
1 2
1 3
1 4
2 4
3 4
4 5
5 6
5 7
7 6
0 0

输出样例

1

1 2
2 4
3 1
3 6
4 3
5 2
5 4
6 4
6 7
7 5
#

2

1 2
2 4
3 1
4 1
4 3
4 5
5 4
5 6
6 7
7 5
#

分析

题目大意是给定一个连通的无向图,现在要将所有的边变成一条单向边或者两条单向边,使任意两点 i、j 均能可达,要求变成的两条单向边的边最少,输出所有的边。
按照题目意思,我们要求的就是一个有向强连通图,图中任意一条边都有可能变成一条单向边,造成原图不连通,所以我们必须保证变化前后图仍然要保持其连通性,很容易地我们就想到了“桥”:

  1. 如果不存在桥,那么这样的图必然是一个极大双联通分量,对于这样的双连通分量,按照定义它要么是孤点,要么是环。孤点自然不必说,对于环来讲,只要我们能取其能成环的单向边就能构成强连通分量。
  2. 如果图中存在桥,那么我们必须保证桥的双向(因为桥连接两个连通块,任何一条边的缺失都会造成不连通),而除去这些桥,剩下的强连通分量都是双连通的,只要和①一样处理即可。

综上,对于这道题我们要先用tarjan求出桥,并将这些桥"固定",然后对除桥以外的强连通分量进行dfs,保留其dfs树的树边即可,考虑到tarjan实际上也是一个dfs树,因此可以将两个思想结合起来,具体可以看下面代码。

源程序

tarjan算法

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm> 
#define MAXN 1005
#define INF 0x3f3f3f3f
using namespace std;
struct Edge{
	int u,v,next;
	int cut;	//为-1待处理,为0不保留,为1保留 
	Edge(){}
	Edge(int u,int v,int next,int cut):u(u),v(v),next(next),cut(cut){}
}edge[MAXN*MAXN*2];
int EdgeCount,head[MAXN];
int n,m,cnt;
int dfn[MAXN],low[MAXN];
void addEdge(int u,int v)
{
	edge[EdgeCount]=Edge(u,v,head[u],-1);	//编号从0开始 
	head[u]=EdgeCount++;
}
void init()
{
	memset(head,-1,sizeof(head));
	memset(dfn,0,sizeof(dfn));
	EdgeCount=cnt=0;
}
void tarjan(int u,int pre)
{
	dfn[u]=low[u]=++cnt;
	for(int i=head[u];i!=-1;i=edge[i].next){
		int v=edge[i].v;
		if(v==pre) continue;
		if(edge[i].cut!=-1)continue;	//该边已处理
		edge[i].cut=1;	//该树边保留
		edge[i^1].cut=0;	
		if(!dfn[v]){	//还未访问过
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			if(low[v]>dfn[u]){	//满足桥的条件,保留桥 
				edge[i].cut=1;
				edge[i^1].cut=1; 
			} 
		}
		else
			low[u]=min(low[u],dfn[v]); 
	}
}
int main()
{
	int cas=0;	//测试组数 
	while(scanf("%d%d",&n,&m)&&(n+m)){
		init();
		for(int i=1;i<=m;i++){
			int u,v;
			scanf("%d%d",&u,&v);
			addEdge(u,v);
			addEdge(v,u);
		}
		tarjan(1,-1);
		printf("%d\n\n",++cas);
		for(int i=0;i<EdgeCount;i++)
			if(edge[i].cut==1)		//是桥或者保留的单向边 
				printf("%d %d\n",edge[i].u,edge[i].v);
		printf("#\n"); 
	}
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值