题目链接:https://vjudge.net/contest/67418#problem/F

题目大意:给你一个图,让你加一条边,使得原图中的桥尽可能的小。(谢谢梁学长的帮忙)

我对重边,tarjan算法中的各个数组的作用,以及需要哪些数组,还有一些不可取的地方。

重边:原来一直以为无向图没有重边,,,在进行无向图的缩点的时候,假设 u- >已经走过了,那么 在不加重边的情况下,v- > u是不能走的。如果加重边了,u->v,这个时候,假设本来v-> u 是桥,但是加了之后就不是桥了。

low数组: 这个数组存的是当前这个数能够到达最早的时间戳,注意low 数组不能用来判断染色。

举个例子:(1,2) (2,1) (2,3) (3,4) (4,2) 这是五条边,如果按照正确的tarjan算法来跑到的话。

1 1 1

2 1 1


3 2 1


4 2 1(具体形式:编号 low数组的值 染色值),可以看到虽然这四个是一个联通块,但是每一个的low并不是都相等,这个地方就可以联想到tarjan的有向这个性质上了。

dfn 数组:这个就是时间戳了。

istack数组:这个数组的作用就是判断哪一些是联通块,哪一些点是在一个缩点里面的。

我觉得tarjan 这三个数组就差不多够了,还有一个细节,在判断已经访问过的点的时候,这个时候需要更新,但是并不能访问已经缩好的点,否则的话,会将已经形成缩点的中的一部分点原来的值覆盖掉。

树的直径

我所理解的树的直径,就是树中最长的一条链。具体实现形式,首选任意选取这个图上的一个点,通过这个点进行bfs,找到最远的点,然后再通过最远的点进行bfs,再找到的长度就是这个树上的最长链了,也就是树的直径。

我自己的证明方法:假设最长链是 u - v, 首先任意选取一个点,如果选取的这个点是最长链上的,那么第一次bfs找到的点一定是u或者v,然后再进行一次bfs的话,肯定能够将最长的链找出来。如果选取的点不是最长链上的,那么在寻找的过程中,肯定也能找到两个端点中的一个,也就是说肯定能找到最长链上的一个点,然后就和第一种情况相同了。

对于这个题

首先缩点是可以理解的,为了让形成的新的图尽可能的小(如果按照原图的话,寻找树的直径的我过程会超时),也就是联通的都形成一个缩点,然后剩下的边就肯定是桥了,这样的话,图就会变得比原来小多了。为什么这个题求最长直径就可以使得减少的桥的数目最多?首先,最长直径是对于缩点后的图来说的,如果把其中两个点连起来,这两个点原来形成的链上的边(也就是桥)都会消失,但是这两个点的相连并不会对其他的桥产生影响,因为该缩点的都缩起来了,所以这个题就需要找一个最长链,才能使得减少的桥数最多。

AC代码:

 #include<iostream>
#include<stack>
#include<stdio.h>
#include<map>
#include<vector>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const int maxn = +;//注意宏定义!!!宏定义相乘会消耗比较多的时间
# define ll long long
# define inf 0x3f3f3f3f
struct node
{
int nex;
int to;
int flag;
} edge[maxn*];
struct point
{
int fr;
int to;
} po[maxn*];
int head[maxn],low[maxn],dfn[maxn],istack[maxn];
int vis[maxn],dis[maxn];
int n,m,k,num,maxx;
int timeindex,col;
stack<int>w;
void init1()
{
memset(low,,sizeof(low));
memset(dfn,,sizeof(dfn));
memset(istack,,sizeof(istack));
while(!w.empty())w.pop();
k=;
timeindex=;
col=;
maxx=;
}
void init2()
{
memset(head,-,sizeof(head));
num=;
}
void addedge(int fr,int to)
{
edge[num].to=to;
edge[num].nex=head[fr];
edge[num].flag=;
head[fr]=num++;
}
void tarjan(int u,int root)
{
low[u]=dfn[u]=++timeindex;
w.push(u);
for(int i=head[u]; i!=-; i=edge[i].nex)
{
int v=edge[i].to;
if(edge[i].flag)continue;
edge[i].flag=edge[i^].flag=;
if(dfn[v]==)
{
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u])
{
po[++k].fr=u;
po[k].to=v;
}
}
else if(istack[v]==)//如果当前访问过的不是在已经形成的联通块里面在可以更新。
{
low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u])
{
int t;
col++;
do
{
t=w.top();
w.pop();
istack[t]=col;
}
while(t!=u);
}
}
int bfs(int t)
{
memset(vis,,sizeof(vis));
memset(dis,,sizeof(dis));
vis[t]=;
queue<int>q;
q.push(t);
int ind=;
while(!q.empty())
{
int top=q.front();
q.pop();
for(int i=head[top]; i!=-; i=edge[i].nex)
{
int u=edge[i].to;
if(vis[u])continue;
vis[u]=;
dis[u]=dis[top]+;
if(dis[u]>maxx)
{
maxx=dis[u];
ind=u;
}
q.push(u);
}
}
return ind;
}
int main()
{
while(~scanf("%d %d",&n,&m)&&(n+m))
{
int t1,t2;
init1();
init2();
for(int i=; i<=m; i++)
{
scanf("%d %d",&t1,&t2);
addedge(t1,t2);
addedge(t2,t1);
}
tarjan(,);
init2();
for(int i=; i<=k; i++)
{
addedge(istack[po[i].fr],istack[po[i].to]);//按照染色的数进行建图
addedge(istack[po[i].to],istack[po[i].fr]);
}
int t=bfs(istack[po[].fr]);
bfs(t);
// cout<<k<<" "<<maxx<<endl;
printf("%d\n",k-maxx);
}
return ;
}

 

F - Warm up HDU - 4612 tarjan缩点 + 树的直径 + 对tajan的再次理解的更多相关文章

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

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

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

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

  3. cf1000E We Need More Bosses (tarjan缩点+树的直径)

    题意:无向联通图,求一条最长的路径,路径长度定义为u到v必须经过的边的个数 如果把强联通分量都缩成一个点以后,每个点内部的边都是可替代的:而又因为这是个无向图,缩完点以后就是棵树,跑两遍dfs求直径即 ...

  4. hdu 4612 边连通度缩点+树的最长路径

    思路:将以桥为分界的所有连通分支进行缩点,得到一颗树,求出树的直径.再用树上的点减去直径,再减一 #pragma comment(linker, "/STACK:1024000000,102 ...

  5. hdu4612 Warm up[边双连通分量缩点+树的直径]

    给你一个连通图,你可以任意加一条边,最小化桥的数目. 添加一条边,发现在边双内是不会减少桥的.只有在边双与边双之间加边才有效.于是,跑一遍边双并缩点,然后就变成一棵树,这样要加一条非树边,路径上的点( ...

  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——————【边双连通分量、树的直径】

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

  8. (求树的直径)Warm up -- HDU -- 4612

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4612 给一个无向图, 加上一条边后,求桥至少有几个: 那我们加的那条边的两个顶点u,v:一定是u,v之 ...

  9. 4612 warm up tarjan+bfs求树的直径(重边的强连通通分量)忘了写了,今天总结想起来了。

    问加一条边,最少可以剩下几个桥. 先双连通分量缩点,形成一颗树,然后求树的直径,就是减少的桥. 本题要处理重边的情况. 如果本来就两条重边,不能算是桥. 还会爆栈,只能C++交,手动加栈了 别人都是用 ...

随机推荐

  1. 爬虫学习之-git拉取远程错误

    本文讲的是把git在最新2.9.2,合并pull两个不同的项目,出现的问题如何去解决 如果合并了两个不同的开始提交的仓库,在新的 git 会发现这两个仓库可能不是同一个,为了防止开发者上传错误,于是就 ...

  2. 【php】set_include_path和get_include_path用法详解

    目的:在框架中方便加载文件 参考:http://blog.sina.com.cn/s/blog_4ce89f200100twbl.html 如果我们没有设置这个值,可能我们需要写一些完全的路径:    ...

  3. 用Python实现求Fibonacci数列的第n项

    1. 背景——Fabonacci数列的介绍(摘自百度百科): 斐波那契数列(Fibonacci sequence),又称黄金分割数列.因数学家列昂纳多·斐波那契(Leonardoda Fibonacc ...

  4. node进程捕捉错误

    var childProcess = require('child_process'); var commitMessage = (function() { var spawn = childProc ...

  5. CF697D-Puzzles

    题目 一棵树,从根节点开始dfs,每层以随机顺序进入每个子节点,问走到每个点的时候期望经过了多少个点. (这里经过多少个点指的是经过多少个不同的点,即经过一个点多次算一个) (其实这个题不如说求期望d ...

  6. BZOJ 1226 学校食堂(状压DP)

    状压DP f(i,j,k)表示前i−1个人已经吃了饭,且在i之后的状态为j的人也吃了饭(用二进制表示后面的状态),最后吃的那个人是i之后的第k个 (注意k可以是负数) 然后 如果j&1=1那么 ...

  7. BZOJ3594 SCOI2014方伯伯的玉米田(动态规划+树状数组)

    可以发现每次都对后缀+1是不会劣的.考虑dp:设f[i][j]为前i个数一共+1了j次时包含第i个数的LIS长度.则f[i][j]=max(f[i][j-1],f[k][l]+1) (k<i,l ...

  8. 题解 P2955 【[USACO09OCT]奇数偶数Even? Odd? 】

    很明显这题是个假入门! 小金羊一不小心点进题解发现了内幕 能看的出来都WA过Unsigned long long int 做题可以用Python,Python的变量虽然 强悍的不行! 但是我们可以用字 ...

  9. P2764 最小路径覆盖问题(网络流24题之一)

    题目描述 «问题描述: 给定有向图G=(V,E).设P 是G 的一个简单路(顶点不相交)的集合.如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖.P 中路径可以从V 的任何一个顶点开 ...

  10. windows 网络共享无法用

    可以远程电脑,但是无法网卡共享 原因是  远程电脑的Server服务停掉了,再开启下就行了