poj3177

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值