热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

poj3177RedundantPaths加入最少的边,使得无向图为一个双连通分支有重边

InordertogetfromoneoftheF(1<F<5,000)grazingfields(whicharenumbered1..F)t
In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1..F) to another field, Bessie and the rest of the herd are forced to cross near the Tree of Rotten Apples. The cows are now tired of often being forced to take a particular path and want to build some new paths so that they will always have a choice of at least two separate routes between any pair of fields. They currently have at least one route between each pair of fields and want to have at least two. Of course, they can only travel on Official Paths when they move from one field to another. 

Given a description of the current set of R (F-1 <= R <= 10,000) paths that each connect exactly two different fields, determine the minimum number of new paths (each of which connects exactly two fields) that must be built so that there are at least two separate routes between any pair of fields. Routes are considered separate if they use none of the same paths, even if they visit the same intermediate field along the way. 

There might already be more than one paths between the same pair of fields, and you may also build a new path that connects the same fields as some other path.

Input

Line 1: Two space-separated integers: F and R 

Lines 2..R+1: Each line contains two space-separated integers which are the fields at the endpoints of some path.

Output

Line 1: A single integer that is the number of new paths that must be built.

Sample Input

7 71 2
2 3
3 4
2 5
4 5
5 6
5 7

Sample Output

2

Hint

Explanation of the sample: 

One visualization of the paths is: 
   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: 
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
 
Every pair of fields is, in fact, connected by two routes. 

It's possible that adding some other path will also solve the problem (like one from 6 to 7). Adding two paths, however, is the minimum.




#include
#include
#include
#include
using namespace std;
//给你一个无向图,要求你加入最少的边,使得最后得到的图为一个双连通分支。
//所谓的双连通分支,即不存在桥的连通分支.
//可以求出所有的桥,把桥删掉。然后把所有的连通分支求出来,显然这些连通分支就是
//原图中的双连通分支。把它们缩成点,然后添上刚才删去的桥,就构成了一棵树。在树上添
//边使得树变成一个双连通分支即可
//只要求输出一共需要添加多少条边,统计度为1的节点(设共有x个),
//然后直接输出(x+1)/2即可.


///此题中有重边
const int maxn=80000;
//hash判重
int hash[maxn];
int repeat[maxn];
int ind[maxn];
void init_hash()
{
    memset(hash,-1,sizeof(hash));
    memset(repeat,0,sizeof(repeat));
    memset(ind,0,sizeof(ind));
}
int ishash(int u,int t,int index)//true 表示已经存在
{
    int tmp=u*10000+t;
    int ret=tmp%maxn;
    while(hash[ret]!=-1&&hash[ret]!=tmp)
    {
        ret=(ret+1)%maxn;
    }
    if(hash[ret]==tmp) return ind[ret];
    hash[ret]=tmp;
    ind[ret]=index;
    return -1;
}
struct edge
{
    int t,index;
    int next;
};
int V,E;
int p[maxn];
edge G[maxn];
int l;
void init()
{
    memset(p,-1,sizeof(p));
    l=0;
}
void addedge(int u,int t,int index,int l)
{
    G[l].t=t;
    G[l].index=index;
    G[l].next=p[u];
    p[u]=l;
}
int isbridge[maxn];
//tarjan 求割点 割边(没有重边)
int cut[maxn];//cut[i]非0表示i是割点
int color[maxn];//颜色:0表示没有访问,1表示正在访问,2表示访问结束
int lowc[maxn];//表示i及i的子孙相连的辈分最高的祖先节点所在的深度
int d[maxn];//表示i节点在树中的深度
int root;//根节点
int fath;//父节点
int pcnt;//割点个数
int egcnt;//割边个数
int egt[maxn];
int egu[maxn];
int egv[maxn];
void dfs(int u,int fath,int deep)
{
    color[u]=1;//正在访问
    lowc[u]=d[u]=deep;//深度
    int tot=0;//子树个数
    for(int i=p[u];i!=-1;i=G[i].next)
    {
        int t=G[i].t,index=G[i].index;
        if(t!=fath&&color[t]==1)
        {
            lowc[u]=min(lowc[u],d[t]);
        }
        if(color[t]==0)
        {
            dfs(t,u,deep+1);
            tot++;//子树加1
            lowc[u]=min(lowc[u],lowc[t]);
            //求割点
            //if((u==root&&tot>1)||(u!=root&&lowc[t]>=d[u])) cut[u]=1;//不能将pscnt++写到这里
            //求割边
            if(lowc[t]>d[u]) //edge[u][t]=true;  u->t是割边
            {
                //判断重边
                if(repeat[index])continue;
                egu[egcnt]=u;
                egv[egcnt]=t;
                egt[egcnt++]=index;
                isbridge[index]=1;
            }
        }
    }
    color[u]=2;
}
void calc()
{
    pcnt=egcnt=0;
    memset(color,0,sizeof(color));
    memset(lowc,0,sizeof(lowc));
    memset(d,0,sizeof(d));
    memset(cut,0,sizeof(cut));
    root=1;
    dfs(1,-1,1);
    //for(int i=1;i<=V;i++) if(cut[i]) pcnt++;
}
//去掉桥边 缩点
int tp[maxn];
edge tG[maxn];
int tl;
void taddedge(int u,int t,int index,int l)
{
    tG[l].t=t;
    tG[l].index=index;
    tG[l].next=tp[u];
    tp[u]=l;
}
int vis[maxn];
int belg[maxn];//节点i属于第几块
void find(int u,int id)
{
    vis[u]=1;belg[u]=id;
    for(int i=p[u];i!=-1;i=G[i].next)
    {
        int t=G[i].t,index=G[i].index;
        if(!isbridge[index]&&!vis[t])
        {
            find(t,id);
        }
    }
}
int Xdu1;
int du[maxn];//重建图后加点的度
void rebuildgraph()
{
    memset(tp,-1,sizeof(tp));
    tl=0;
    int det=0;//缩点后节点个数
    memset(vis,0,sizeof(vis));
    memset(belg,0,sizeof(belg));
    memset(du,0,sizeof(du));
    for(int i=1;i<=V;i++)
    {
        if(!vis[i])
        {
            find(i,++det);
        }
    }
    for(int i=0;i    {
        int u=egu[i],v=egv[i];
        int tu=belg[u],tv=belg[v];
        taddedge(tu,tv,i+1,tl++);
        taddedge(tv,tu,i+1,tl++);
    }
    //无向图 故最后度要除以2
    for(int i=1;i<=det;i++)
    {
        for(int j=tp[i];j!=-1;j=tG[j].next)
        {
            du[i]++;
            du[tG[j].t]++;
        }
    }
    Xdu1=0;
    for(int i=1;i<=det;i++)
    {
        if(du[i]/2==1) Xdu1++;
    }
}
int main()
{
    while(scanf("%d%d",&V,&E)==2)
    {
        init();
        init_hash();
        memset(isbridge,0,sizeof(isbridge));
        for(int i=1;i<=E;i++)
        {
            int u,t,index=i;
            scanf("%d%d",&u,&t);
            if(u>t) swap(u,t);
            int ret;
            if((ret=ishash(u,t,index))!=-1)
            {
                repeat[ret]=1;
                continue;
            }
            addedge(u,t,index,l++);
            addedge(t,u,index,l++);
        }
        calc();
        //删除桥边 缩点
        rebuildgraph();
        printf("%d\n",(Xdu1+1)/2);
    }
    return 0;
}

推荐阅读
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 本文介绍了C函数ispunct()的用法及示例代码。ispunct()函数用于检查传递的字符是否是标点符号,如果是标点符号则返回非零值,否则返回零。示例代码演示了如何使用ispunct()函数来判断字符是否为标点符号。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 学习Java异常处理之throws之抛出并捕获异常(9)
    任务描述本关任务:在main方法之外创建任意一个方法接收给定的两个字符串,把第二个字符串的长度减1生成一个整数值,输出第一个字符串长度是 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
author-avatar
mobiledu2502883787
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有