您的位置:首页 > 其它

poj3177

2016-07-19 21:56 148 查看
POJ3177

描述

为了从F(1≤F≤5000)个牧场(从1到F进行编号)中的一个到达另一个牧场,Bessie和他的牛只能穿过烂苹果林。这些牛现在都累了,不想老走这个特殊的小路而是想要建一些新的小路,这样它们就有至少两种选择在任意牧场之间穿梭。在任意的两个牧场之间,它们现在至少有一条路径而他们想要至少有2条路径。

现在给出建好的R(F-1≤R≤10000)条路,每条都连接了两个不同的牧场,确定新修道路的最小数量(每个也是连接两个牧场)使得任意两个牧场之间都能有两条路连通。道路都是不同的,不会有相同的小路,即使他们在沿路的中间会碰到相同的牧场。

这里可能已经有一对牧场有超过一条小路的,并且你可能也会修一条新的另外的小路来连接相同的牧场。

输入

第一行:两个空格隔开的整数:F和R

第二到第R+1行:每行包括两个空格隔开的整数,表示一些小路端点所在的牧场。

输出

第一行:一个单独的整数,表述新修小道的数量

样例输入

7 7

1 2

2 3

3 4

2 5

4 5

5 6

5 7

样例输出

2

提示

例子的解释:

一个形象化的小路:

1 2 3

+—+—+

| |

| |

6 +—+—+ 4

/ 5

/

/

7 +

建一个从1到6和从4到7的新路可以满足条件。
1 2 3
+—+—+
| |

| |

6 +—+—+ 4

/ 5 :

/ :

/ :

7 + - - - -

检查一些路径:

1 – 2: 1 –> 2 and 1 –> 6 –> 5 –> 2

1 – 4: 1 –> 2 –> 3 –> 4 and 1 –> 6 –> 5 –> 4

3 – 7: 3 –> 4 –> 7 and 3 –> 2 –> 5 –> 7

可以看出每对牧场都是由两条路连接的

有可能加一些别的下路也能解决这个问题(比如6到7加一条道)。然而加两条已经是最小了。

。。。。我根本没看懂提示的样子,还好知道这是构建双联通分量的题目,

首先求出所有的桥,然后删除这些桥边,剩下的每个连通块都是一个双连通子图。把每个双连通子图收缩为一个顶点,再把桥边加回来,最后的这个图一定是一棵树,边连通度为1。

统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf。则至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,所以至少添加的边数就 是(leaf+1)/2

嗯,这些话不是我说的;

#include<cstdio>
#include<cstdlib>
#include<cstring>
struct edge{int v,next;}e[10010];
int n,en,t=0,index,m,a,b,vi;
int dfn[5005],low[5005],scc[5005],first[5005];//scc记录缩点后个点的各种度(出度+入度)
void addedge(int a, int b)
{
e[en].v = b;
e[en].next = first[a];
first[a] = en++;
}
void clean()
{
memset(scc,0,sizeof(scc));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(first,-1,sizeof(first));
index=en=0;
}
void tarjan(int rt, int fa)
{
dfn[rt] = low[rt] = ++ index;
for (int i =first[rt]; i != -1; i = e[i].next)
{
vi = e[i].v;
if (!dfn[vi])
{
tarjan(vi, rt);
low[rt] = low[rt]<low[vi]?low[rt]:low[vi];
}
else if (vi != fa)
{
low[rt] = low[rt]<dfn[vi]?low[rt]:dfn[vi];
}
}//这里好像是照抄的上一题的。。
}
void out()
{
for (int i = 1; i <= n; ++ i)
{
for (int j = first[i]; j != -1; j = e[j].next)
{
vi = e[j].v;
if (low[vi] != low[i])
{
//如果把所有的桥边删掉,剩下的可以化成一个个点
//只有被桥边分开的联通分量low值不同,看作两个不同的点
//但这本身又有一条边,于是这是桥边,两个联通分量的度加一
//应该是遍历时 另一个点也会遍历到这条边,所以每次自己加一就够了
scc[low[i]] ++;
}//好六啊,反正不是我能想出来的
}
}
for (int i = 1; i <= n; ++ i)
{
if (scc[i] == 1)
{
t ++;//如果有度为1的,记录下来,给他们每个连一条边,就成了双联通
}
}
printf("%d\n", (t + 1) / 2);//先是他们之间互联
}
int main()
{

clean();
scanf("%d %d", &n, &m) ;
for (int i = 1; i <=m; ++ i)
{scanf("%d %d", &a, &b);addedge(a,b);addedge(b,a);}//多么有爱的输入
tarjan(1, 1);
out();
//这代码写时又让我欲仙欲死,在和ppt 网上的代码对比之后确定了算法没问题,但就是结果错了
//最后把网上的代码改成我的一步步纠错,却是把所有的i都加了一个int
//嗯,大概是全局变量在各个函数调用的过程中死掉了
return 0;
}


4000
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  割点 双联通