HDU 4612 Warm up —— (缩点 + 求树的直径)
题意:一个无向图,问建立一条新边以后桥的最小数量。
分析:缩点以后,找出新图的树的直径,将这两点连接即可。
但是题目有个note:两点之间可能有重边!而用普通的vector保存边的话,用v!=fa的话是没办法让重边访问的,因此,使用数组模拟邻接表的方法来储存边。
这样,只要访问了一条边以后,令E[i].vis=E[i^1].vis=1即可,这样可以防止无向图的边和重边搞混。原理就是按位异或,一个奇数^1变为比它小1的偶数,反之亦然:如5^1=4,4^1=5。
具体见代码:
- #include <stdio.h>
- #include <algorithm>
- #include <string.h>
- #include <stack>
- #include <vector>
- #include <set>
- #include <queue>
- //#pragma comment(linker, "/STACK:1024000000,1024000000")
- using namespace std;
- const int N = +;
- int n,m,dfn[N],low[N];
- int head[N];
- int tot=;
- int dfs_clock;
- int bridge;
- int belong[N];
- int scc_cnt;
- int maxd;
- bool vis[N];
- vector<int> G[N];
- stack<int> S;
- struct edge
- {
- int v;
- int vis;
- int nxt;
- }E[*+];
- void addEdge(int u,int v)
- {
- E[tot].v=v;
- E[tot].vis=;
- E[tot].nxt=head[u];
- head[u]=tot++;
- E[tot].v=u;
- E[tot].vis=;
- E[tot].nxt=head[v];
- head[v]=tot++;
- }
- void tarjan(int u)
- {
- dfn[u]=low[u]=++dfs_clock;
- S.push(u);
- for(int i=head[u];i!=-;i=E[i].nxt)
- {
- int v = E[i].v;
- if(E[i].vis) continue;
- E[i].vis=E[i^].vis=;
- if(!dfn[v])
- {
- tarjan(v);
- low[u]=min(low[u],low[v]);
- if(low[v]>dfn[u]) bridge++;
- }
- else if(!belong[v])
- {
- low[u]=min(low[u],dfn[v]);
- }
- }
- if(dfn[u]==low[u])
- {
- scc_cnt++;
- for(;;)
- {
- int x = S.top();S.pop();
- belong[x] = scc_cnt;
- if(x==u) break;
- }
- }
- }
- void init()
- {
- memset(head,-,sizeof(head));
- tot=;
- memset(dfn,,sizeof(dfn));
- dfs_clock=;
- bridge=;
- memset(belong,,sizeof(belong));
- scc_cnt=;
- maxd=;
- for(int i=;i<=n;i++) G[i].clear();
- memset(vis,,sizeof(vis));
- }
- //找到树的直径
- void findMaxDeep(int u,int deep)
- {
- vis[u]=;
- maxd=max(maxd,deep);
- for(int i=;i<G[u].size();i++)
- {
- int v = G[u][i];
- if(!vis[v])
- {
- findMaxDeep(v,deep+);
- }
- }
- }
- //用bfs来找到一个叶子节点
- int findRoot()
- {
- queue<int> Q;
- Q.push();
- vis[]=;
- int last=;
- while(!Q.empty())
- {
- int x = Q.front();Q.pop();
- for(int i=;i<G[x].size();i++)
- {
- int v = G[x][i];
- if(!vis[v])
- {
- Q.push(v);
- vis[v]=;
- last=v;
- }
- }
- }
- return last;
- }
- void solve()
- {
- for(int i=;i<=n;i++) if(!dfn[i]) tarjan(i);
- //重新建图
- for(int i=;i<=n;i++)
- {
- for(int j=head[i];j!=-;j=E[j].nxt)
- {
- int v = E[j].v;
- int x = belong[i];
- int y = belong[v];
- if(x!=y)
- {
- G[x].push_back(y);
- G[y].push_back(x);
- }
- }
- }
- int root=findRoot();
- memset(vis,,sizeof(vis));
- findMaxDeep(root,);
- printf("%d\n",bridge-maxd);
- }
- int main()
- {
- while(scanf("%d%d",&n,&m)==)
- {
- if(n== && m==) break;
- init();
- for(int i=;i<=m;i++)
- {
- int u,v;
- scanf("%d%d",&u,&v);
- addEdge(u,v);
- }
- solve();
- }
- }
但是奇怪的是,重新建图以后找叶子节点的时候,这里用G[x].size()==2不能够实现。讲道理,原理上是没错的,尽管后来发现如果缩点后只有一个点的话是个例外,但是即使排除了这个特殊情况仍然不行,,,这个问题也留着以后探讨吧。。
HDU 4612 Warm up —— (缩点 + 求树的直径)的更多相关文章
- 4612 warm up tarjan+bfs求树的直径(重边的强连通通分量)忘了写了,今天总结想起来了。
问加一条边,最少可以剩下几个桥. 先双连通分量缩点,形成一颗树,然后求树的直径,就是减少的桥. 本题要处理重边的情况. 如果本来就两条重边,不能算是桥. 还会爆栈,只能C++交,手动加栈了 别人都是用 ...
- F - Warm up - hdu 4612(缩点+求树的直径)
题意:有一个无向连通图,现在问添加一条边后最少还有几个桥 分析:先把图缩点,然后重构图为一棵树,求出来树的直径即可,不过注意会有重边,构树的时候注意一下 *********************** ...
- HDU 4612 Warm up(双连通分量缩点+求树的直径)
思路:强连通分量缩点,建立一颗新的树,然后求树的最长直径,然后加上一条边能够去掉的桥数,就是直径的长度. 树的直径长度的求法:两次bfs可以求,第一次随便找一个点u,然后进行bfs搜到的最后一个点v, ...
- hdu4612 无向图中随意加入一条边后使桥的数量最少 / 无向图缩点+求树的直径
题意如上,含有重边(重边的话,俩个点就能够构成了边双连通). 先缩点成树,在求数的直径,最远的连起来,剩下边(桥)的自然最少.这里学习了树的直径求法:第一次选随意起点U,进行bfs,到达最远的一个点v ...
- hdu4612 无向图中任意添加一条边后使桥的数量最少 / 无向图缩点+求树的直径
题意如上,含有重边(重边的话,俩个点就可以构成了边双连通). 先缩点成树,在求数的直径,最远的连起来,剩下边(桥)的自然最少.这里学习了树的直径求法:第一次选任意起点U,进行bfs,到达最远的一个点v ...
- (求树的直径)Warm up -- HDU -- 4612
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4612 给一个无向图, 加上一条边后,求桥至少有几个: 那我们加的那条边的两个顶点u,v:一定是u,v之 ...
- hdoj 4612 Warm up【双连通分量求桥&&缩点建新图求树的直径】
Warm up Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Total Su ...
- HDU 4612 Warm up (边双连通分量+缩点+树的直径)
<题目链接> 题目大意:给出一个连通图,问你在这个连通图上加一条边,使该连通图的桥的数量最小,输出最少的桥的数量. 解题分析: 首先,通过Tarjan缩点,将该图缩成一颗树,树上的每个节点 ...
- HDU 4612——Warm up——————【边双连通分量、树的直径】
Warm up Time Limit:5000MS Memory Limit:65535KB 64bit IO Format:%I64d & %I64u Submit Stat ...
随机推荐
- windows下安装mongoDB(zip版)
windows下安装mongoDB(zip版) 下面说明如何在win10下用zip包安装好mongoDB数据库 首先要先从网上下载mongoDB的zip包 http://dl.mongodb.org/ ...
- 【原创】编程基础之Jekins
Jenkins 2.164.2 官方:https://jenkins.io 一 简介 Build great things at any scale The leading open source a ...
- Django rest-framework框架-认证组件的简单实例
第一版 : 自己写函数实现用户认证 #models from django.db import models #用户表 class UserInfo(models.Model): user_type_ ...
- mysql 添加grant权限
GRANT USAGE ON *.* TO 'xxxx'@'x.%.%.%' WITH GRANT OPTION;
- HTML 5浏览器端数据库
HTML 5浏览器端数据库为什么要使用浏览器端数据库:随着浏览器处理能力的增强,越来越多的双喜鸟网站开始考虑在客户端存储大量的数据,这可以减少用户从服务器获取数据的等待时间. 1.本地存储-本地存储可 ...
- WinPE基础知识之导出表
// 导出的东西包括函数(变量.类)地址,序号,函数(变量.类)名 typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; // ...
- 本地套接字-本地socket
本地套接字简单应用场景 一 #服务端--简单 import socket import os a='sock_file' if os.path.exists(a): os.remove(a) s=so ...
- java lambda 所有列求和
今天做东西的时候遇到一个需求,求list集合所有列的求和.折腾半天也没有搞出来,网上大部分都是单列求和就像下面这样的,其他都差多,什么 min,max avg count 只得到了number这个属性 ...
- Linux搭建.net core CI/CD环境
一.简介 微服务开发中自动化.持续化工程十分重要,在成熟的CI/CD环境中项目团队可以灵活分配,大大提供团队效率.如果还不了解什么是CI/CD,可以先查看相关文章,这里主要介绍环境的搭建,相关原理就不 ...
- PAT Basic 1064 朋友数 (20 分)
如果两个整数各位数字的和是一样的,则被称为是“朋友数”,而那个公共的和就是它们的“朋友证号”.例如 123 和 51 就是朋友数,因为 1+2+3 = 5+1 = 6,而 6 就是它们的朋友证号.给定 ...