POJ3177 Redundant Paths
2016-05-06 20:34
246 查看
一. 原题链接:http://poj.org/problem?id=3177
二. 题目大意:给一个无向图,要求添加一些边使得任意两点之间最少有2条不同的路径,不同的路径指的是没有公共边的路径。
三. 思路:
1. 我们知道任意一个边双连通分量,满足之中的两两点之间有2条不同的路径,其实利用Tarjan算法求边双连通分量也体现这种结果。假设父亲为u,子女为v,如果low[v]<=dfn[u],说明v可以回到u以及u以上的点,而u到v已经有一条路径了,于是u,v在同一个边双连通分量里面。
2. 于是问题转化成求出多少个边双连通分量,单独一个节点算一个,然后把每个双连通分量看成一个点,也就是缩点,这时候变成了一棵树(无回路),树的叶子节点到叶子节点直接肯定只有一条路径,因为它是叶子节点,要走到另一个叶子节点一定要先返回根,于是我们要做的只需要把叶子节点全部连成一个边双连通图,而n个叶子节点,需要(n+1)/2条边,其实叶子节点就是度为1的边。
3. 于是我们可以用Tarjan求出原图的各个边双连通分量,用并查集把同一连通分量的节点(low[u] <= dfn[v])集合起来,同时求出所有的桥(low[u]>dfn[v]),就是缩点后的树的边。用一个数组blong把所有的边双连通分量标号,然后扫一遍,数出各个双连通分量的度,度为1的个数就是树的叶子节点。然后结果就出来了。
四.代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <cmath>
#include <cstdlib>
using namespace std;
const int MAX_N = 5008,
INF = 0x3f3f3f3f;
class AdjList
{
public:
struct Edge
{
int v, next;
}edges[MAX_N*MAX_N];
int head[MAX_N], cnt;
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
}
void addEdge(int u, int v)
{
edges[cnt].v = v,
edges[cnt].next = head[u];
head[u] = cnt++;
}
};
class FUset
{
public:
int pre[MAX_N];
void init()
{
memset(pre, -1, sizeof(pre));
}
int Find(int x)
{
int root = x, save;
for(; pre[root] >= 0; root = pre[root]);
for(save = pre[x]; x != root; pre[x] = root, x = save, save = pre[x]);
return root;
}
void Union(int x, int y)
{
int Rootx = Find(x), Rooty = Find(y);
int temp = pre[Rootx] + pre[Rooty];
if(pre[Rootx] > pre[Rooty]){
pre[Rootx] = Rooty;
pre[Rooty] = temp;
}
else{
pre[Rooty] = Rootx;
pre[Rootx] = temp;
}
}
};
class Tarjan
{
public:
AdjList graph;
FUset FU;
int bridge[MAX_N][2], low[MAX_N], dfn[MAX_N],
nBridge, nodeNum, belong[MAX_N];//every node belong to which double connection.
bool visited[MAX_N];
void init(int n)
{
nodeNum = n;
nBridge = 0;
graph.init();
FU.init();
memset(visited, 0, sizeof(visited));
memset(belong, -1, sizeof(belong));
}
void dfs(int pre, int u, int depth)
{
visited[u] = true;
low[u] = dfn[u] = depth;
bool toPre = false;
int i, v;
for(i = graph.head[u]; i != -1; i = graph.edges[i].next){
v = graph.edges[i].v;
if(!visited[v]){
dfs(u, v, depth + 1);
low[u] = min(low[u], low[v]);
if(low[v] <= dfn[u])//able to return, belong to the same double connection.
FU.Union(u, v);
if(low[v] > dfn[u])//unable to return, (u, v) is a bridge.
bridge[nBridge][0] = u, bridge[nBridge++][1] = v;
}
else if(v != pre || toPre)
low[u] = min
4000
(low[u], dfn[v]);
else if(v == pre)
toPre = true;
}
}
int doubleConnection()
{
int i, root, res = 0;
dfs(-1, 1, 1);
for(i = 1; i <= nodeNum; i++){
root = FU.Find(i);
if(belong[root] == -1)
belong[root] = res++;
belong[i] = belong[root];
}
return res;
}
int getRes()
{
int cnt[MAX_N] = {0}, i, num, u, v, res = 0;
num = doubleConnection();
for(i = 0; i < nBridge; i++){
u = bridge[i][0];
v = bridge[i][1];
cnt[belong[u]]++, cnt[belong[v]]++;
}
for(i = 0; i < num; i++)
if(1 == cnt[i])
res++;
return res;
}
}G;
int main()
{
//freopen("in.txt", "r", stdin);
int i, u, v, F, R;
while(~scanf("%d%d", &F, &R)){
G.init(F);
for(i = 0; i < R; i++){
scanf("%d%d", &u, &v);
G.graph.addEdge(u, v);
G.graph.addEdge(v, u);
}
printf("%d\n", (G.getRes()+1)/2);
}
return 0;
}
二. 题目大意:给一个无向图,要求添加一些边使得任意两点之间最少有2条不同的路径,不同的路径指的是没有公共边的路径。
三. 思路:
1. 我们知道任意一个边双连通分量,满足之中的两两点之间有2条不同的路径,其实利用Tarjan算法求边双连通分量也体现这种结果。假设父亲为u,子女为v,如果low[v]<=dfn[u],说明v可以回到u以及u以上的点,而u到v已经有一条路径了,于是u,v在同一个边双连通分量里面。
2. 于是问题转化成求出多少个边双连通分量,单独一个节点算一个,然后把每个双连通分量看成一个点,也就是缩点,这时候变成了一棵树(无回路),树的叶子节点到叶子节点直接肯定只有一条路径,因为它是叶子节点,要走到另一个叶子节点一定要先返回根,于是我们要做的只需要把叶子节点全部连成一个边双连通图,而n个叶子节点,需要(n+1)/2条边,其实叶子节点就是度为1的边。
3. 于是我们可以用Tarjan求出原图的各个边双连通分量,用并查集把同一连通分量的节点(low[u] <= dfn[v])集合起来,同时求出所有的桥(low[u]>dfn[v]),就是缩点后的树的边。用一个数组blong把所有的边双连通分量标号,然后扫一遍,数出各个双连通分量的度,度为1的个数就是树的叶子节点。然后结果就出来了。
四.代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <cmath>
#include <cstdlib>
using namespace std;
const int MAX_N = 5008,
INF = 0x3f3f3f3f;
class AdjList
{
public:
struct Edge
{
int v, next;
}edges[MAX_N*MAX_N];
int head[MAX_N], cnt;
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
}
void addEdge(int u, int v)
{
edges[cnt].v = v,
edges[cnt].next = head[u];
head[u] = cnt++;
}
};
class FUset
{
public:
int pre[MAX_N];
void init()
{
memset(pre, -1, sizeof(pre));
}
int Find(int x)
{
int root = x, save;
for(; pre[root] >= 0; root = pre[root]);
for(save = pre[x]; x != root; pre[x] = root, x = save, save = pre[x]);
return root;
}
void Union(int x, int y)
{
int Rootx = Find(x), Rooty = Find(y);
int temp = pre[Rootx] + pre[Rooty];
if(pre[Rootx] > pre[Rooty]){
pre[Rootx] = Rooty;
pre[Rooty] = temp;
}
else{
pre[Rooty] = Rootx;
pre[Rootx] = temp;
}
}
};
class Tarjan
{
public:
AdjList graph;
FUset FU;
int bridge[MAX_N][2], low[MAX_N], dfn[MAX_N],
nBridge, nodeNum, belong[MAX_N];//every node belong to which double connection.
bool visited[MAX_N];
void init(int n)
{
nodeNum = n;
nBridge = 0;
graph.init();
FU.init();
memset(visited, 0, sizeof(visited));
memset(belong, -1, sizeof(belong));
}
void dfs(int pre, int u, int depth)
{
visited[u] = true;
low[u] = dfn[u] = depth;
bool toPre = false;
int i, v;
for(i = graph.head[u]; i != -1; i = graph.edges[i].next){
v = graph.edges[i].v;
if(!visited[v]){
dfs(u, v, depth + 1);
low[u] = min(low[u], low[v]);
if(low[v] <= dfn[u])//able to return, belong to the same double connection.
FU.Union(u, v);
if(low[v] > dfn[u])//unable to return, (u, v) is a bridge.
bridge[nBridge][0] = u, bridge[nBridge++][1] = v;
}
else if(v != pre || toPre)
low[u] = min
4000
(low[u], dfn[v]);
else if(v == pre)
toPre = true;
}
}
int doubleConnection()
{
int i, root, res = 0;
dfs(-1, 1, 1);
for(i = 1; i <= nodeNum; i++){
root = FU.Find(i);
if(belong[root] == -1)
belong[root] = res++;
belong[i] = belong[root];
}
return res;
}
int getRes()
{
int cnt[MAX_N] = {0}, i, num, u, v, res = 0;
num = doubleConnection();
for(i = 0; i < nBridge; i++){
u = bridge[i][0];
v = bridge[i][1];
cnt[belong[u]]++, cnt[belong[v]]++;
}
for(i = 0; i < num; i++)
if(1 == cnt[i])
res++;
return res;
}
}G;
int main()
{
//freopen("in.txt", "r", stdin);
int i, u, v, F, R;
while(~scanf("%d%d", &F, &R)){
G.init(F);
for(i = 0; i < R; i++){
scanf("%d%d", &u, &v);
G.graph.addEdge(u, v);
G.graph.addEdge(v, u);
}
printf("%d\n", (G.getRes()+1)/2);
}
return 0;
}
相关文章推荐
- C经典 结构图的三种定义方式
- cocos-js,时间相关的处理
- Hadoop HDFS Tools
- 手工MAVEN建立WEBAPP项目并打包部署
- lightoj 1074 spfa判断负环
- 日记帐导入-来源为“采购”日记帐导入失败问题
- 文件传输基础—Java IO流 FileInputStream 的运用
- deep learning 练习1 线性回归练习
- 虚拟化_KVM
- 代理模式:控制对对象的访问
- 前端-js-this
- CentOS允许/禁止ping的方法
- IE7-span折行问题
- HDU 3639 Hawk-and-Chicken tarjan缩点+dfs
- Android去除题栏
- C++实验5——数组分离
- POJ 1573 Robot Motion
- java集合09--Map架构
- 状态模式:把执行的动作封装在跟状态有关的类里,由状态来决定动作该如何执行
- (学习进度表)【第十周】