ACM: 无向图的强连通分量 图论题 p…
2016-05-19 23:21
267 查看
Street
Directions
Description
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.
Input
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.
Output
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.
Sample Input
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
Sample Output
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) 只输出i j 或则 j i即可.
解题思路:
1. 求无向图的边连通分量, 在同一个连通分量里面, 保留单向的边即可
即: 构成一个强连通分量. 而对于不同的连通分量, 期间的边必须是双向边,
才能够保证点与点之间相互连通.
2. tarjan算法学习。。。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define MAX 1005
const int INF = (1<<29);
struct node
{
int v;
int next;
bool vis;
}edges[MAX*10];
int n, m;
int first[MAX], num;
int bcc[MAX*10], bcnt, deep[MAX*10], low[MAX*10];
int stack[MAX*10], top;
bool instack[MAX*10], vis[MAX*10];
int start, fa_start;
void init()
{
for(int i = 0; i <= n+2;
++i)
{
first[i] = -1; // 初始化邻接表头
deep[i] = low[i] = 0; //
初始化深度和从i出发经过的顶点形成路径,
// 所能到达的最小深度"优先"编号
bcc[i] = 0;
vis[i] = instack[i] =
false;
}
num = top = bcnt = 0;
}
inline void add(int u,int v)
{
edges[num].v = v;
edges[num].vis = false;
edges[num].next = first[u];
first[u] = num++;
}
void read_graph()
{
int u, v;
int i;
init();
for(i = 0; i < m; ++i)
{
scanf("%d
%d",&u,&v);
add(u,v);
add(v,u);
}
start = 1; //初始化起始点编号.
fa_start = 0; //初始化起始点的父亲节点编号
}
void tarjan(int u,int fa,int dfsindex)
{
deep[u] = low[u] = dfsindex++;
instack[u] = true;
stack[top++] = u;
for(int e = first[u]; e != -1; e =
edges[e].next)
{
int v = edges[e].v;
if(v != fa)
{
if( !deep[v]
)
{
tarjan(v,u,dfsindex);
if(low[u]
> low[v]) low[u] = low[v];
}
else
if(instack[v] && deep[v]
< low[u])
{
low[u]
= deep[v];
}
}
}
if(deep[u] == low[u])
{
bcnt++; //计数器
int v;
do
{
v =
stack[top-1];
top--;
instack[v] =
0;
bcc[v] =
bcnt;
}while( v != u );
}
}
void solve(int u,int fa)
{
for(int e = first[u]; e != -1; e =
edges[e].next)
{
int v = edges[e].v;
if(v != fa)
{
if(bcc[u] ==
bcc[v] && !edges[e].vis)
{
printf("%d
%d\n",u,v);
}
else
if(bcc[u] != bcc[v] &&
!edges[e].vis)
{
printf("%d
%d\n",u,v);
printf("%d
%d\n",v,u);
}
edges[e].vis
= true;
edges[e^1].vis
= true;
if( !vis[v]
)
{
vis[v]
= true;
solve(v,u);
}
}
}
}
int main()
{
int k = 1;
// freopen("input.txt","r",stdin);
while(scanf("%d
%d",&n,&m) != EOF)
{
if(n == 0
&& m == 0) break;
printf("%d\n\n",k++);
read_graph();
tarjan(start,fa_start,1);
//start: 起始点, fa_start: 起始点的父亲, 1:初始化深度为1;
vis[start] = 1;
solve(start,fa_start);
printf("#\n");
}
return 0;
}
Directions
Description
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.
Input
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.
Output
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.
Sample Input
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
Sample Output
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) 只输出i j 或则 j i即可.
解题思路:
1. 求无向图的边连通分量, 在同一个连通分量里面, 保留单向的边即可
即: 构成一个强连通分量. 而对于不同的连通分量, 期间的边必须是双向边,
才能够保证点与点之间相互连通.
2. tarjan算法学习。。。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define MAX 1005
const int INF = (1<<29);
struct node
{
int v;
int next;
bool vis;
}edges[MAX*10];
int n, m;
int first[MAX], num;
int bcc[MAX*10], bcnt, deep[MAX*10], low[MAX*10];
int stack[MAX*10], top;
bool instack[MAX*10], vis[MAX*10];
int start, fa_start;
void init()
{
for(int i = 0; i <= n+2;
++i)
{
first[i] = -1; // 初始化邻接表头
deep[i] = low[i] = 0; //
初始化深度和从i出发经过的顶点形成路径,
// 所能到达的最小深度"优先"编号
bcc[i] = 0;
vis[i] = instack[i] =
false;
}
num = top = bcnt = 0;
}
inline void add(int u,int v)
{
edges[num].v = v;
edges[num].vis = false;
edges[num].next = first[u];
first[u] = num++;
}
void read_graph()
{
int u, v;
int i;
init();
for(i = 0; i < m; ++i)
{
scanf("%d
%d",&u,&v);
add(u,v);
add(v,u);
}
start = 1; //初始化起始点编号.
fa_start = 0; //初始化起始点的父亲节点编号
}
void tarjan(int u,int fa,int dfsindex)
{
deep[u] = low[u] = dfsindex++;
instack[u] = true;
stack[top++] = u;
for(int e = first[u]; e != -1; e =
edges[e].next)
{
int v = edges[e].v;
if(v != fa)
{
if( !deep[v]
)
{
tarjan(v,u,dfsindex);
if(low[u]
> low[v]) low[u] = low[v];
}
else
if(instack[v] && deep[v]
< low[u])
{
low[u]
= deep[v];
}
}
}
if(deep[u] == low[u])
{
bcnt++; //计数器
int v;
do
{
v =
stack[top-1];
top--;
instack[v] =
0;
bcc[v] =
bcnt;
}while( v != u );
}
}
void solve(int u,int fa)
{
for(int e = first[u]; e != -1; e =
edges[e].next)
{
int v = edges[e].v;
if(v != fa)
{
if(bcc[u] ==
bcc[v] && !edges[e].vis)
{
printf("%d
%d\n",u,v);
}
else
if(bcc[u] != bcc[v] &&
!edges[e].vis)
{
printf("%d
%d\n",u,v);
printf("%d
%d\n",v,u);
}
edges[e].vis
= true;
edges[e^1].vis
= true;
if( !vis[v]
)
{
vis[v]
= true;
solve(v,u);
}
}
}
}
int main()
{
int k = 1;
// freopen("input.txt","r",stdin);
while(scanf("%d
%d",&n,&m) != EOF)
{
if(n == 0
&& m == 0) break;
printf("%d\n\n",k++);
read_graph();
tarjan(start,fa_start,1);
//start: 起始点, fa_start: 起始点的父亲, 1:初始化深度为1;
vis[start] = 1;
solve(start,fa_start);
printf("#\n");
}
return 0;
}
相关文章推荐
- ACM: 最近公共祖先问题 图论题
- ACM: 广搜 图论题 poj 1708
- ACM: 闭包问题 图论题 (书上练习)
- ACM: 图论题 poj 1161 (把图重建成…
- ACM: 图论题 poj 1135
- ACM: 简单 动态规划题 toj 1509 (…
- Largest prime 尤拉托斯展那筛选法
- ACM: 动态规划题 toj 1508
- ACM: 训练题 动态规划题 (蛮有意思…
- ACM: 动态规划体 toj 1408
- ACM: 背包问题 动态规划题 toj 133…
- ACM: 动态规划题 toj1328
- ACM: 动态规划题 poj 2241
- ACM: 最大连续和O(N) 动态规划 toj…
- ACM: 动态规划题 toj 1072
- ACM: 动态规划题+剪枝 toj 3904
- ACM: 博弈题 poj 1067
- ACM: 贪心法 poj 1700
- ACM: 树状DP 动态规划题 poj 1463 …
- 第一章 答疑摘选