HDU-4612 Warm up,tarjan求桥缩点再求树的直径!注意重边
虽然网上题解这么多,感觉写下来并不是跟别人竞争访问量的,而是证明自己从前努力过,以后回头复习参考!
题意:n个点由m条无向边连接,求加一条边后桥的最少数量。
思路:如标题,tarjan算法求出所有的桥,然后连通的缩成点,用桥连接这些点,很容易发现这是一颗生成树,我们再加一条边必然成环,要使得桥的数量最少,就得使得这个环中的边最多。于是找这棵树最长的一条链。即树的直径。然后桥的数量减去直径既是答案。为什么不用加一呢,因为加的那条边使得成环不算桥。
总结:此题做了两天,如果全力去做的话估计的一天,不算上课和其中做的题。还不是自己做的,对,参考了网上的很多博客,不过这不是重点,学到东西才是王道。
一、看懂题后知道要缩点也知道要求最长的那条链,但我并没有做过求树的直径,于是在百度了树的直径。很多博客并没有给出详细的证明。于是花了点时间理解。
二、鉴于此题有重边问题,于是和WR交流了一下,我想着怎么快速hash一下处理重边,然而数据范围可能至使超int,用map函数可能也有超时,不然数据小都用领接矩阵这不水题了。WR说领接表重边没关系,于是我放弃了惯用的vector,对于领接表生疏的原因是初学的时候能力欠缺,晦涩难懂,后来接触了vector几乎没用过领接表。其实对于他们的差别我还是很清楚的,不过出题方应该不会卡这点时间。然后参考者网上的代码修修改改A了,突然意识到代码这和vector式没多大差别,然后打算用vector来一发,不负众望,果断WA。。。。不过已经找到了一组数据debug。
const int N=2e5+10;
int d,key,c,ti,tot,top,bridge,n,m;
int head[N],low[N],dfn[N],Stack[N],belong[N],vis[N];
vector<int>g[N];
struct node
{
int to,next,f;
} e[N*10];
void init()
{
c=bridge=ti=top=tot=0;
for(int i=0; i<N; i++)
{
head[i]=-1;
Stack[i]=low[i]=dfn[i]=0;
belong[i]=vis[i]=0;
g[i].clear();
}
memset(e,0,sizeof(e));
}
void add(int u,int v)
{
e[tot].to=v;
e[tot].next=head[u];
head[u]=tot++;
}
void tarjan(int u)
{
int v;
low[u]=dfn[u]=++ti;
vis[u]=1;
Stack[top++]=u;
for(int i=head[u]; i+1; i=e[i].next)
{
v=e[i].to;
if(e[i].f) continue;
e[i].f=e[i^1].f=1;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
if(dfn[u]<low[v]) bridge++;
}
else if(vis[v]&&dfn[v]<low[u]) low[u]=dfn[v];
}
if(dfn[u]==low[u])
{
c++;
do
{
v=Stack[--top];
vis[v]=0;
belong[v]=c;
}
while(v!=u);
}
// puts("1");
}
void dfs(int k,int pre,int dep)
{
vis[k]=1;
if(dep>d)
{
d=dep;
key=k;
}
for(int i=0; i<g[k].size(); i++)
{
int v=g[k][i];
if(!vis[v]) dfs(v,k,dep+1);
}
}
void work()
{
for(int i=1; i<=n; i++) if(!dfn[i]) tarjan(i);
for(int i=1; i<=n; i++)
for(int j=head[i]; j!=-1; j=e[j].next)
{
int s=belong[i],t=belong[e[j].to];
if(s!=t)
{
g[s].push_back(t);
g[t].push_back(s);
}
// puts("1");
}
d=key=0;
memset(vis,0,sizeof(vis));
vis[1]=1;
dfs(1,1,0);
d=0;
memset(vis,0,sizeof(vis));
vis[key]=1;
dfs(key,key,0);
// printf("%d %d\n",bridge,d);
printf("%d\n",bridge-d);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
if(n==m&&m==0) return 0;
init();
int u,v;
for(int i=0; i<m; i++)
{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
work();
}
return 0;
}//搜索并没有调用栈,于是不担心手动加栈问题。
限于时间问题,明天将vector的问题呈上!
以下呈上上述问题的解答:纯属个人理解,若有不同看法或更高的见解敬请联(教)系(教)我。
上面说了已经有一组样例能推翻vector存图的重边问题了。嘿嘿,是在讨论区看的:
6 6
1 2
2 3
3 4
4 5
2 6
6 2
输出:0。 而不是 1
于是我模拟了一下这组样例终于发现问题所在了,我们可以观察领接表存图的时候相当于把无向图拆分成两个有向图然后建立起来,我们建立的时候可以发现一条无向边存了两次,他们的边的序号是相邻的(参见add()函数),于是在tarjan的时候只需求一次就行了,而另一条i^1只需标记起来即可。但若出现重边,这意味着重边将至少被存4次,我们相邻的可以标记起来,遍历时遇到直接continue,但总是会通过next找到另一个点,这样就形成了一个环了,染色时颜色是一样的。所以巧妙地解决了重边不是桥的问题!
而用vector存图时,我们是用一个father来标记是否访问过的,如果两个点成环即重边出现,我们是通过一个点找到另一个点的,即出发点作为father,那么在遍历邻点的时候不管邻点的领接图里存了多少个相同的father,都会被continue,邻点出发无法再回到father了,和单向边就没有区别了,这样这条边肯定会被当成桥。至于解决容我再三思三思!
给出vector存图的Wrong Answer:上组样例无法通过
const int N=2e5+10;
int d,key,c,ti,top,bridge,n,m;
int low[N],dfn[N],Stack[N],belong[N],vis[N];
vector<int>g[N];
vector<int>g1[N];
void init()
{
c=top=ti=key=d=bridge=0;
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(Stack,0,sizeof(Stack));
memset(vis,0,sizeof(vis));
memset(belong,0,sizeof(vis));
for(int i=0; i<N; i++)
{
g[i].clear();
g1[i].clear();
}
}
void tarjan(int u,int pre)
{
int v;
low[u]=dfn[u]=++ti;
vis[u]=1;
Stack[top++]=u;
for(int i=0; i<g[u].size(); i++)
{
v=g[u][i];
if(v==pre) continue;//当前点无法回到pre点,这条边必然成桥
if(!dfn[v])
{
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u]) bridge++;
}
else if(vis[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
c++;
do
{
v=Stack[--top];
vis[v]=0;
belong[v]=c;
}
while(u!=v);
}
}
void dfs(int k,int dep)
{
vis[k]=1;
if(dep>d)
{
d=dep;
key=k;
}
for(int i=0; i<g1[k].size(); i++)
{
int v=g1[k][i];
if(!vis[v]) dfs(v,dep+1);
}
}
void work()
{
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,i);
for(int i=1; i<=n; i++)
for(int j=0; j<g[i].size(); j++)
{
int v=g[i][j];
if(belong[v]!=belong[i])
{
g1[belong[v]].push_back(belong[i]);
g1[belong[i]].push_back(belong[v]);
}
}
memset(vis,0,sizeof(vis));
dfs(1,0);
memset(vis,0,sizeof(vis));
dfs(key,0);
printf("%d %d\n",bridge,d);
printf("%d\n",bridge-d);
}
int main()
{
while(~scanf("%d%d",&n,&m),n+m)
{
init();
int u,v;
for(int i=0; i<m; i++)
{
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
work();
}
return 0;
}
HDU-4612 Warm up,tarjan求桥缩点再求树的直径!注意重边的更多相关文章
- HDU 4612 Warm up(双连通分量缩点+求树的直径)
思路:强连通分量缩点,建立一颗新的树,然后求树的最长直径,然后加上一条边能够去掉的桥数,就是直径的长度. 树的直径长度的求法:两次bfs可以求,第一次随便找一个点u,然后进行bfs搜到的最后一个点v, ...
- HDU 4612 Warm up (边双连通分量+缩点+树的直径)
<题目链接> 题目大意:给出一个连通图,问你在这个连通图上加一条边,使该连通图的桥的数量最小,输出最少的桥的数量. 解题分析: 首先,通过Tarjan缩点,将该图缩成一颗树,树上的每个节点 ...
- HDU 4612 Warm up tarjan缩环+求最长链
Warm up Problem Description N planets are connected by M bidirectional channels that allow instant ...
- HDU 4612 Warm up tarjan 树的直径
Warm up 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=4612 Description N planets are connected by ...
- HDU 4612 Warm up(Tarjan)
果断对Tarjan不熟啊,Tarjan后缩点,求树上的最长路,注意重边的处理,借鉴宝哥的做法,开标记数组,标记自己的反向边. #pragma comment(linker, "/STACK: ...
- 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 有重边缩点+树的直径
题目链接 Warm up Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Tot ...
- Hdu 4612 Warm up (双连通分支+树的直径)
题目链接: Hdu 4612 Warm up 题目描述: 给一个无向连通图,问加上一条边后,桥的数目最少会有几个? 解题思路: 题目描述很清楚,题目也很裸,就是一眼看穿怎么做的,先求出来双连通分量,然 ...
- HDU 4612 Warm up —— (缩点 + 求树的直径)
题意:一个无向图,问建立一条新边以后桥的最小数量. 分析:缩点以后,找出新图的树的直径,将这两点连接即可. 但是题目有个note:两点之间可能有重边!而用普通的vector保存边的话,用v!=fa的话 ...
随机推荐
- Django分组查询
先补充两个知识点: 1.group by 大前提:可以按照任意字段分组,但是最好是按照分辨度比较低的来分组(重复比较多的分辨度比较低). group by分组可以单独使用,不搭配其他条件. 分组的字段 ...
- UITabBarController、导航控制器、控制器关系
UITabBarController与UINavigationController类似,UITabBarController也可以用来控制多个页面导航,用户可以在多个视图控制器之间移动,并可以定制屏幕 ...
- Android笔记--Bitmap
Android | Bitmap解析 Android中Bitmap是对图像的一种抽象.通过他可以对相应的图像进行剪裁,旋转,压缩,缩放等操作.这里循序渐进的一步步了解Bitmap的相关内容. 先了解B ...
- Java MVC 增删改查 实例
需求:实现增加新部门的功能,对应数据库表示Oracle的dept表 一.Java MVC 增 实现: 1.视图层(V):注册部门 deptAdd.jsp 在注册新部门页面只需输入“部门名称”和“城市” ...
- FATAL org.apache.hadoop.hdfs.server.datanode.DataNode: Initialization failed for Block pool <registering> (Datanode Uuid unassigned) service to controller/192.168.1.183:9000. Exiting. java.io.IOExcep
2018-01-09 09:47:38,297 FATAL org.apache.hadoop.hdfs.server.datanode.DataNode: Initialization failed ...
- 关于用终端运行php来测试推送的问题
照网上的方法,合并好了证书的pem,密码也是对的,然后也写好了推送用的php文件,在终端里php这个文件,报错报错内容是:Warning: stream_socket_client(): SSL op ...
- FIBON高精度
#include<stdio.h> #include<string.h> int u,n; ],b[],h[]; ],y[],z[]; int main() { char s( ...
- ArcGis server连接oracle
ArcGIG server连接Oracle 目录--gis服务器--添加arcgis server 下一步: 身份验证为在arcgis server manager 中的管理员登录密码和账户 对于服务 ...
- 参考别人的代码写的aes加密,记录一下(AES,ECB模式,填充PKCS5Padding,数据块128位,偏移量无,以hex16进制输出)
package org.jimmy.autosearch2019.test; import java.security.SecureRandom; import javax.crypto.Cipher ...
- Linux命令基础操作--vim 归档 压缩 分区 格式化 挂载 Innode
1 将用户信息数据库文件和组信息数据库文件纵向合并为一个文件/1.txt(覆盖) 使用 cat命令将查看的文件合并输出到/1.txt 这里的关键:定位到文件,如果后面加上/后被认为是目录 分为两步,先 ...