题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4612

题意:一个包含n个节点m条边的无向连通图(无自环,可能有重边)。求添加一条边后最少剩余的桥的数目。

思路:要想尽可能地消灭桥,那么添加的这条边一定是连通了最多的BCC。

  所以首先进行双连通分量分解,并记录桥的数目;然后将同属一个BCC的点缩成一个,代之以block序号,以block序号为点将原图重构为一棵树。

  最后求树的直径,桥的数目减去树的直径即为答案。

整体框架是学习了 http://www.cnblogs.com/kuangbin/archive/2013/07/25/3214879.html 的代码。一些细节是自己想的。

学习到的缩点的姿势:在BCC分解后,为每条边是否为桥打上了标记,同时belong数组已记录了每个点所属的连通块号(关节点每次不退栈,而归属于它所找到的最后一个BCC);这时遍历一遍所有点,如果点 u 所邻接的某条边 i 是桥,那么这条边一定是重构出树中的边,即这条边的终点为 v ,假如用vector<int>形的邻接表 G 存新图,此时应在u和v各自所属的连通块之间加一条边,即G[belong[u]].push_back(belong[v])。

学习到的处理重边的姿势:先把所有边按字典序排序,此时重边必然紧邻,所以addEdge时判一下相邻的边是否相同即可决定这条边是否打重边标记。

这里我用两重while循环、一个flagDup标记以及两个指针cur, i来处理,有点像尺取法:i 与 cur相同的话一直向前走,当遇到第一个 i != cur 时,通过判flagDup来决定当前从开始cur 到 i 之前的边是否打重边标记。开始在边界判断上出了点问题,注意内层循环要保证 i < m。

由于是无向图,同一条边在两个端点各登记一次,所以在边数组里有两个副本,但可以保证两条边的序号仅相差1,这样也可以通过与 1 异或方便地找到另一个副本。

 #include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define CLEAR(A, X) memset(A, X, sizeof(A))
#define REP(N) for(int i=0; i<(N); i++)
#define REPE(N) for(int i=1; i<=(N); i++)
#define FREAD(FN) freopen((FN), "r", stdin)
#define pb(a) push_back(a) using namespace std; const int MAX_N = ;
const int MAX_M = ; struct Edge
{
int v, next;
bool isBrg;
bool more;//重边
}edges[MAX_M]; int head[MAX_N], numE;
int low[MAX_N], dfn[MAX_N], S[MAX_N], belong[MAX_N];
int clock, topS;
int block;
bool inStack[MAX_N];
int numBrg; void addEdge(int u, int v, bool isDup){
edges[numE].v = v;
edges[numE].next = head[u];
edges[numE].isBrg = ;
edges[numE].more = isDup;//是否重边
head[u] = numE++; //反向边,序号仅差1
edges[numE].v = u;
edges[numE].next = head[v];
edges[numE].isBrg = ;
edges[numE].more = isDup;
head[v] =numE++;
} void bcc(int u, int p, bool isDup){
low[u] = dfn[u] = ++clock;
S[topS++] = u;
inStack[u] = ;
for(int i=head[u]; i != -; i = edges[i].next){
int v = edges[i].v;
if(v == p && (!isDup)) continue;//parent and no duplicate
if(!dfn[v]){//tree
bcc(v, u, edges[i].more);
low[u] = min(low[u], low[v]);
if(low[v] > dfn[u]){//bridge
numBrg++;
edges[i].isBrg = ;
edges[i^].isBrg = ;
}
}else if(inStack[v])//backward or parent
low[u] = min(low[u], dfn[v]);//include parent
}
if(low[u] == dfn[u]){//new bcc
block++;
int t;
do{
t = S[--topS];
inStack[t] = ;
belong[t] = block;
}while(t != u);
}
} void init(){
numE = ;
numBrg = ;
CLEAR(head, -);
CLEAR(low, ); CLEAR(dfn, );
CLEAR(inStack, );
CLEAR(belong, );
clock = block = topS = ;
} vector<int> G[MAX_N];//缩点后的新图
int dep[MAX_N]; void dfs(int u){
for(int i=; i<G[u].size(); i++){
int v = G[u][i];
if(dep[v] == -){
dep[v] = dep[u]+;
dfs(v);
}
}
} int n, m;
struct Node
{
int u, v;
}nodes[MAX_M];
bool cmp(Node a, Node b){
if(a.u == b.u) return a.v < b.v;
return a.u < b.u;
}
bool same(Node a, Node b){
if(a.u == b.u && a.v == b.v) return true;
return false;
} int main()
{
FREAD("4612.txt");
while(~scanf("%d%d", &n, &m) && n && m){
init();
for(int i=; i<m; i++){
int u, v;
scanf("%d%d", &u, &v);
if(u > v) swap(u, v);
nodes[i].u = u; nodes[i].v = v;
}
sort(nodes, nodes+m, cmp);//将重边聚到一起
int cur = , i = ;
int flagDup = ;
while(cur < m){
while(i<m && same(nodes[cur], nodes[i])){
flagDup = ;//与cur重
i++;
}
if(flagDup) addEdge(nodes[cur].u, nodes[cur].v, );
else addEdge(nodes[cur].u, nodes[cur].v, );
//printf("add edge %d %d\n", nodes[cur].u, nodes[cur].v);
flagDup = ;
cur = i++;
} bcc(, , );//0->1虚拟边
REPE(block) G[i].clear();//block个节点的图
REPE(n){//以block为编号,[1,block]建新图
for(int j=head[i]; j!=-; j=edges[j].next){
if(edges[j].isBrg){
int u = i;
int v = edges[j].v;
G[belong[u]].pb(belong[v]);//缩点
}
}
}
CLEAR(dep, -);
dep[] = ;//以1为根
dfs();
int deepest = , maxDep = -;
REPE(block){//找到最深的
if(dep[i] > dep[deepest]){
maxDep = dep[deepest];
deepest = i;
}
}
CLEAR(dep, -);
dep[deepest] = ;//以deepest为根
dfs(deepest);
int len = ;//树的直径
REPE(block) len = max(len, dep[i]); printf("%d\n", numBrg - len);
}
return ;
}

这道题从MLE改到WA改到RE再改到TLE,最终弃掉自己的版本学习了bin神的。发现他的实现很真实地反映了思路,比如bcc时,增加父节点p和边属性isDup两个参数,这样就通过v!=p && (!isDup)把无重边的父子边过滤掉,剩下真的后向边和有重边的父子边。我在从思路到代码的转换上还需要多加练习。

【HDU 4612 Warm up】BCC 树的直径的更多相关文章

  1. Hdu 4612 Warm up (双连通分支+树的直径)

    题目链接: Hdu 4612 Warm up 题目描述: 给一个无向连通图,问加上一条边后,桥的数目最少会有几个? 解题思路: 题目描述很清楚,题目也很裸,就是一眼看穿怎么做的,先求出来双连通分量,然 ...

  2. HDU 4612 Warm up tarjan 树的直径

    Warm up 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=4612 Description N planets are connected by ...

  3. F - Warm up - hdu 4612(缩点+求树的直径)

    题意:有一个无向连通图,现在问添加一条边后最少还有几个桥 分析:先把图缩点,然后重构图为一棵树,求出来树的直径即可,不过注意会有重边,构树的时候注意一下 *********************** ...

  4. F - Warm up HDU - 4612 tarjan缩点 + 树的直径 + 对tajan的再次理解

    题目链接:https://vjudge.net/contest/67418#problem/F 题目大意:给你一个图,让你加一条边,使得原图中的桥尽可能的小.(谢谢梁学长的帮忙) 我对重边,tarja ...

  5. HDU 4612——Warm up——————【边双连通分量、树的直径】

    Warm up Time Limit:5000MS     Memory Limit:65535KB     64bit IO Format:%I64d & %I64u Submit Stat ...

  6. hdu 4612 Warm up 有重边缩点+树的直径

    题目链接 Warm up Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Tot ...

  7. HDU 4612 Warm up(双连通分量缩点+求树的直径)

    思路:强连通分量缩点,建立一颗新的树,然后求树的最长直径,然后加上一条边能够去掉的桥数,就是直径的长度. 树的直径长度的求法:两次bfs可以求,第一次随便找一个点u,然后进行bfs搜到的最后一个点v, ...

  8. HDU 4612 Warm up (边双连通分量+缩点+树的直径)

    <题目链接> 题目大意:给出一个连通图,问你在这个连通图上加一条边,使该连通图的桥的数量最小,输出最少的桥的数量. 解题分析: 首先,通过Tarjan缩点,将该图缩成一颗树,树上的每个节点 ...

  9. HDU 4612 Warm up —— (缩点 + 求树的直径)

    题意:一个无向图,问建立一条新边以后桥的最小数量. 分析:缩点以后,找出新图的树的直径,将这两点连接即可. 但是题目有个note:两点之间可能有重边!而用普通的vector保存边的话,用v!=fa的话 ...

随机推荐

  1. 源码分析之struts1自定义方法的使用与执行过程

    最近有人问我,你做项目中用户的一个请求是怎么与struts1交互的,我说请求的url中包含了action的名字和方法名,这样就可以找到相应方法,执行并返回给用户了. 他又问,那struts1中有什么方 ...

  2. 读书笔记-HBase in Action-第二部分Advanced concepts-(2)Coprocessor

    Coprocessor是HBase 0.92.0引入的特性.使用Coprocessor.能够将一些计算逻辑下推到HBase节点,HBase由一个单纯的存储系统升级为分布式数据处理平台. Coproce ...

  3. LoadRuner性能测试之内存分析方法及步骤(Windows)

    1.首先观察Available  Mbytes(可用内存),至少要>=1/2的内存空间 2.然后观察Pages/sec值是不是很大 3.再观察Page  Faules/sec是不是很大,其值表示 ...

  4. ssh命令

    使用ssh命令登陆远程系统 ssh [ip/address] -l [登陆用户名] 如: ssh www.xyz.cn -l root

  5. 第一次用IIS发布网站时遇到的两个问题

    1.  配置错误 说明: 在处理向该请求提供服务所需的配置文件时出错.请检查下面的特定错误详细信息并适当地修改配置文件. 分析器错误消息: 无法识别的属性“targetFramework”.请注意属性 ...

  6. 使用 Fluent API 配置/映射属性和类型2

    1.将多个实体类映射到数据库中的一个表 要将多个实体映射到一个数据库表需要满足: a. 两个实体必须是一对一关系 b.两个实体共享一个主键 public class MyContext:DbConte ...

  7. Android-Context的IO功能

    如何将应用数据保存到本地文件?如何从本地文件加载数据到应用中?我实现的步骤是: 应用(java数据)<--org.json-->JSONString<--Context.IO--&g ...

  8. VC防止程序被多次运行 互斥体方法

    BOOL CXXXApp::InitInstance() //函数内添加代码 HANDLE hMutex=CreateMutex(NULL,TRUE,"test"); // 用于检 ...

  9. Struts2+Spring3+Hibernate3+Maven构建(基于Eclipse)

    长时间不做后台了,整理一下资料,以便翻阅. Eclipse.JDK安装略…… Maven下载地址:http://maven.apache.org/download.cgi 版本比较新的Eclipse基 ...

  10. git命令简图

    基本概念 版本库(repository)=.git目录 工作区(working)=当前工作的目录 暂存区(stage)=临时缓存 图示 仓库操作 分支操作 工作区操作 参考链接(相当好) http:/ ...