题意:加一条边最少还剩多少桥。
这题一想了一会就想到了缩点后求树的直径,第一遍敲,树的直径我用的是一个两遍dfs,并且没考虑重边,但报的TLE,第二遍换bfs写不过仍然没考虑重边 wa了。
考虑重边,用map或者set标记,重边肯定不是桥,结果MLE了。
期间再百度了一下树的直径,一般有三种方法,我觉得最简单的一种应该是只要我之前的第一遍dfs,然后利用性质:直径一定是某点在其子树下最大和次大之和。换回dfs写法,当然还是MLE。
看题解,学习下重边怎么办。
kuangbin是直接把所以的边排序,直接把重边找出来,(其实我的思路也是这样,不过找的方法不好,STL导致MLE了)但这样做代码写起来很多很乱。
更好的方法是:在dfs的时候,让v可以通过重边回到父亲节点,而不能通过原来那条边回到父亲节点。
在没有重边的tarjan写法里是遇到父亲就直接continue。现在就是要想办法只continue掉没有重边的父亲。
只要给每条边弄个标记:访问过的边不再访问即可(不是访问过的点不再访问)。这样应该是能实现的,也看见有人是这样的写的,但是我自己写就是不知道哪错了,于是最后学了Alex大神的写法,给一条无向边编号(一条无向边等于两条有向边),在tarjan里第二个参数就设为这个编号,如果(u,v)之间的编号和上一条相同(说明是同一条无向边,相当于要回到父亲)则continue,如果存在重边,编号是不一样的,则不会continue。
但是,做了一天脑袋有点晕,我特么的把Stack数组声明成了bool类型的。见鬼似得查错查了几小时,弱小的心灵已经有阴影了。
另外这题还要手动扩栈:加这个
#pragma comment(linker, "/STACK:1024000000,1024000000")
所以:
1、考虑重边
2、手动扩栈
虽然做的很恶心,但是仍然学到了。
【代码】
#include <map>
#include <set>
#include <list>
#include <stack>
#include <vector>
#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int N = 200010;
const int M = 3000010;
struct Edge
{
int next;
int to;
int id;
}edge[M], edge2[M];
int head[N];
int head2[N];
int low[N],belong[N],DFN[N];
int instack[N],Stack[N];
int br_cnt, n, m;
int tot, tot2, maxs, TimeN, top, sccnum;
void addedge(int u,int v,int id)
{
edge[tot] = {head[u],v,id};
head[u] = tot++;
}
void addedge2(int u, int v)
{
edge2[tot2] = {head2[u],v,0};
head2[u] = tot2++;
}
void init()
{
TimeN = sccnum = top = tot = tot2 = maxs = br_cnt = 0;
memset ( head, -1, sizeof(head) );
memset (head2, -1, sizeof(head2) );
memset (DFN, 0, sizeof(DFN) );
memset ( instack, 0, sizeof(instack) );
}
void tarjan(int u, int fa)
{
Stack[top++] = u;
instack[u] = 1;
DFN[u] = low[u] = ++TimeN;
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if (fa == edge[i].id) continue;
if ( !DFN[v] )
{
tarjan(v, edge[i].id);
low[u] = min(low[u], low[v]);
if (DFN[u] < low[v])
br_cnt++;
}
else if(instack[v])
low[u] = min(DFN[v], low[u]);
}
if (DFN[u] == low[u])
{
sccnum++;
int v;
do
{
v = Stack[--top];
instack[v] = 0;
belong[v] = sccnum;
}while(u!=v);
}
}
int mx[N],smx[N];
void dfs(int u,int fa) //直接等于某点最大加次大
{
mx[u] = smx[u] = 0 ;
for(int i=head2[u]; ~i; i=edge2[i].next)
{
int v=edge2[i].to;
if(v==fa) continue;
dfs(v,u);
int tmp = mx[v] + 1;
if(tmp >= mx[u])
{
smx[u] = mx[u];
mx[u] = tmp;
}
else if(tmp > smx[u])
smx[u] = tmp;
}
}
void solve()
{
tarjan(1, -1);
for (int u = 1; u <= n; ++u) //缩点建新图
{
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if (belong[u] != belong[v])
addedge2(belong[u],belong[v]);
}
}
dfs(1,-1);
for(int i=1;i<=n;i++) maxs = max(maxs , mx[i]+smx[i]);
printf("%d\n", br_cnt - maxs);
}
int main()
{
while (~scanf("%d%d", &n, &m) && (n||m))
{
init();
int u, v;
for (int i = 1; i <= m; ++i)
{
scanf("%d%d", &u, &v);
addedge(u, v, i);
addedge(v, u, i);
}
solve();
}
return 0;
}
本文介绍了一个关于图论的问题,即如何通过添加一条边来最小化图中桥的数量。文章详细讨论了处理该问题的具体算法实现,包括考虑重边的影响、使用DFS寻找树的直径以及手动扩大栈空间等技巧。
729

被折叠的 条评论
为什么被折叠?



