您的位置:首页 > 其它

POJ 3352 Road Construction (边双连通分量)

2014-09-19 10:43 543 查看
题目类型 边双连通分量

题目意思
给出一个 n 个点 m 条边的无向连通图(n,m<=1000) 问至少要添加多少条无向边使得 删除原图中任意一条边后原图依然连通

解题方法

根据题意 显然如果删除原图中的 桥 就会使原图不连通 那么首先把原图中的 边双连通分量看成一个点(即缩点) 然后点与点之间的边即为桥

则原问题转化成给出一棵若干个点的树问把树对应的图变为一个边双连通图最少需要添加多少条边

具体方法 -> Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)

结果为树的叶子结点数 + 1 再除 2

参考代码 - 有疑问的地方在下方留言 看到会尽快回复的

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>

using namespace std;

const int maxn = 1000 + 10;

struct Edge {
int u, v;
};

int pre[maxn], dfs_clock, bcc_cnt;
int bcc[maxn], d[maxn];
int GG[maxn][2], n;
bool g[maxn][maxn];

int dfs(int u, int fa) {
int lowu = pre[u] = dfs_clock++;
for( int i=0; i<n; i++ ) {
if(g[u][i] == false) continue;
int v = i;
if(pre[v] == false) {
int lowv = dfs(v, u);
lowu = min(lowu, lowv);
if(lowv > pre[u]) {
g[u][v] = g[v][u] = false;
}
}
else if(pre[v] < pre[u] && v != fa) {
lowu = min(lowu, pre[v]);
}
}
return lowu;
}

void find_cc() { // 用tarjan算法求出所有桥
memset(pre, 0, sizeof(pre));
dfs_clock = 0;
for( int i=0; i<n; i++ ) {
if(pre[i] == false) dfs(i, -1);
}
}

void dfs(int u) { // 求不经过桥的连通分量 即边双连通分量
bcc[u] = bcc_cnt;
for( int i=0; i<n; i++ ) {
if(g[u][i] == false || bcc[i] != -1) continue;
dfs(i);
}
}

int main() {
freopen("in", "r", stdin);
int m;
while(scanf("%d%d", &n, &m) != EOF) {
memset(g, 0, sizeof(g));
for( int i=0; i<m; i++ ) {
int u, v;
scanf("%d%d", &u, &v);
u--, v--;
GG[i][0] = u, GG[i][1] = v;
g[u][v] = g[v][u] = true;
}
find_cc();
bcc_cnt = 0;
memset(bcc, -1, sizeof(bcc));
for( int i=0; i<n; i++ ) {
if(bcc[i] == -1) dfs(i);
bcc_cnt++;
}
memset(d, 0, sizeof(d));
for( int i=0; i<m; i++ ) {
int u = GG[i][0], v = GG[i][1];
if(bcc[u] != bcc[v]) {
d[bcc[u]]++; d[bcc[v]]++;
}
}
int ans = 0;
for( int i=0; i<bcc_cnt; i++ ) if(d[i] == 1) ans++; // 度数为1的点即为叶子结点
printf("%d\n", (ans+1)/2);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: