您的位置:首页 > 其它

[POJ 3177]Redundant Paths[边双连通][Tarjan][缩点]

2016-03-01 01:04 441 查看
题目链接:[POJ 3177]Redundant Paths[边双连通][Tarjan][缩点]
题意分析:

给出一幅含有重边的无向图,问至少连多少条边,使得图中任意两个点u、v都有u->v的路径,和v->u的路径,且两者没有相同的边。

解题思路:

任意一个边双连通子图中两点是任意可达的,这样我们可以把这样的一个边双连通子图缩成一个点。将原图所有的双连通子图缩成一个点后,新图就是一棵树了,现在就变为了在树上添加几条树枝,使得整棵树变成边双连通图。答案是:(度数为1的点 + 1) / 2。

关于tarjan,推荐这两个链接:
1.有向图强连通分量的Tarjan算法,2.图的割点、桥与双连通分支

个人感受:

今天一天的知识量爆炸啊啊啊啊啊boom~~~然后是一丢丢好容易混淆的概念TAT

目前tarjan也仅仅是懂了个大概,入了个门。

具体代码如下:

#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<string>
#define ll long long
#define pr(x) cout << #x << " = " << (x) << '\n';
using namespace std;

const int INF = 0x7f7f7f7f;
const int MAXN = 5e3 + 111;

struct Edge {
int to, next;
bool cut;
}edge[MAXN];
int head[MAXN], tot;
int low[MAXN], dfn[MAXN], Stack[MAXN], Belong[MAXN];
int index, top;
int block;
bool inStack[MAXN];
int bridge;

void add_edge(int u, int v) {
edge[tot].to = v;
edge[tot].cut = 0;
edge[tot].next = head[u];
head[u] = tot++;
}

void Tarjan(int u, int fa) {
int v;
low[u] = dfn[u] = ++index;
Stack[top++] = u;
inStack[u] = 1;

for (int i = head[u]; ~i; i = edge[i].next) {
v = edge[i].to;
if (v == fa) continue;
if (!dfn[v]) {
Tarjan(v, u);
low[u] = min(low[u], low[v]);
if (low[v] > dfn[u]) { // low[v] < dfn[u]说明子结点能到达u的父节点,也就是形成环了
++bridge;
edge[i].cut = edge[i^1].cut = 1;
}
}
else if (inStack[v])
low[u] = min(low[u], dfn[v]);
}

if (low[u] == dfn[u]) { // 缩点
++block;
do {
v = Stack[--top];
inStack[v] = 0;
Belong[v] = block;
}while (v != u);
}
}

int deg[MAXN];

int main()
{
int f, r, u, v;
tot = 0;
scanf("%d%d", &f, &r);
memset(head, -1, sizeof head);
while (r --) {
scanf("%d%d", &u, &v);
add_edge(u, v);
add_edge(v, u);
}

memset(dfn, 0, sizeof dfn);
memset(inStack, 0, sizeof inStack);
index = top = block = 0;

Tarjan(1, 0);

int ans = 0;
memset(deg, 0, sizeof deg);
for (int i = 1; i <= f; ++i) {
for (int j = head[i]; ~j; j = edge[j].next) {
if (edge[j].cut) ++deg[Belong[i]]; // 新图中,割边就是连结所有点的边
}
}

for (int i = 1; i <= block; ++i) if (deg[i] == 1) ++ans;

printf("%d\n", (ans + 1) / 2);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: