标签:
| Time Limit: 1000MS | Memory Limit: 65536K | |
| Total Submissions: 10798 | Accepted: 4626 |
Description
Input
Output
Sample Input
7 7 1 2 2 3 3 4 2 5 4 5 5 6 5 7
Sample Output
2
Hint
1 2 3
+---+---+
| |
| |
6 +---+---+ 4
/ 5
/
/
7 +
Building new paths from 1 to 6 and from 4 to 7 satisfies the conditions. 1 2 3
+---+---+
: | |
: | |
6 +---+---+ 4
/ 5 :
/ :
/ :
7 + - - - -
Check some of the routes: 以下解析来自:女神的博客
斌神博客上有个不错的总结:斌神的博客
大致题意:
为了保护放牧环境,避免牲畜过度啃咬同一个地方的草皮,牧场主决定利用不断迁移牲畜进行喂养的方法去保护牧草。然而牲畜在迁移过程中也会啃食路上的牧草,所以如果每次迁移都用同一条道路,那么该条道路同样会被啃咬过度而遭受破坏。
现在牧场主拥有F个农场,已知这些农场至少有一条路径连接起来(不一定是直接相连),但从某些农场去另外一些农场,至少有一条路可通行。为了保护道路上的牧草,农场主希望再建造若干条道路,使得每次迁移牲畜时,至少有2种迁移途径,避免重复走上次迁移的道路。已知当前有的R条道路,问农场主至少要新建造几条道路,才能满足要求?
解题思路:
“使得每次迁移牲畜时,至少有2种迁移途径,避免重复走上次迁移的道路。”就是说当吧F个农场看作点、路看作边构造一个无向图G时,图G不存在桥。
那么可以建立模型:
给定一个连通的无向图G,至少要添加几条边,才能使其变为双连通图。
当图G存在桥(割边)的时候,它必定不是双连通的。桥的两个端点必定分别属于图G的两个【边双连通分量】,一旦删除了桥,这两个【边双连通分量】必定断开,图G就不连通了。但是如果在两个【边双连通分量】之间再添加一条边,桥就不再是桥了,这两个【边双连通分量】之间也就是双连通了。
那么如果图G有多个【边双连通分量】呢?至少应该添加多少条边,才能使得任意两个【边双连通分量】之间都是双连通(也就是图G是双连通的)
1、 首先要找出图G的所有【边双连通分量】。
2、 把每一个【边双连通分量】都看做一个点(即【缩点】)
3、 问题再次被转化为“至少在缩点树上增加多少条树边,使得这棵树变为一个双连通图”。
首先知道一条等式:
若要使得任意一棵树,在增加若干条边后,变成一个双连通图,那么
至少增加的边数 =( 这棵树总度数为1的结点数 + 1 )/ 2。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#define maxn 5010
#define maxm 20010
using namespace std;
int n, m;
struct node {
int u, v, next;
};
node edge[maxm];
//缩点后形成树,每个点的度数
int du[maxn];
int head[maxn], cnt;
int low[maxn], dfn[maxn];
//Belong数组的值是 1 ~ ebc_block
int Stack[maxn], Belong[maxn];
int ebc_block;//边双连通块数
int dfs_clock;
int top;//模拟栈的指针
bool Instack[maxn];
void init(){
cnt = 0;
memset(head, -1, sizeof(head));
}
void addedge(int u, int v){
edge[cnt] = {u, v, head[u]};
head[u] = cnt++;
}
void getmap(){
while(m--){
int a, b;
scanf("%d%d", &a, &b);
addedge(a, b);
addedge(b, a);
}
}
void tarjan(int u, int pre){
int v;
low[u] = dfn[u] = ++dfs_clock;
Stack[top++] = u;
Instack[u] = true;
int have = 1;
for(int i = head[u]; i != -1; i = edge[i].next){
v = edge[i].v;
if(have && v == pre){//去重边
have = 0;
continue;
}
if(!dfn[v]){
tarjan(v, u);
low[u] = min(low[u], low[v]);
}
else if(Instack[v])
low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u]){
ebc_block++;
do{
v = Stack[--top];
Instack[v] = false;
Belong[v] = ebc_block;
}
while(v != u);
}
}
void suodian(){
memset(du, 0, sizeof(du));
for(int i = 0; i < cnt; i += 2 ){
int u = Belong[edge[i].u];
int v = Belong[edge[i].v];
if(u != v)
du[u]++, du[v]++;
}
}
void find(){
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(Instack, false, sizeof(Instack));
memset(Belong, 0, sizeof(Belong));
dfs_clock = 0;
ebc_block = 0;
top = 0;
tarjan(1, -1);//连通图
}
void solve(){
int ans = 0;
if(ebc_block == 1){
printf("0\n");
return ;
}
for(int i = 1; i <= ebc_block; ++i)
if(du[i] == 1) ans++;
printf("%d\n", (ans + 1) / 2);
}
int main (){
while(scanf("%d%d", &n, &m) != EOF){
init();
getmap();
find();
suodian();
solve();
}
return 0;
}版权声明:本文为博主原创文章,未经博主允许不得转载。
POJ 3177--Redundant Paths【无向图增加最少的边成为边双连通图 && tarjan求ebc && 缩点构造缩点树】
标签:
原文地址:http://blog.csdn.net/hpuhjh/article/details/47752065