[Poj]3177——双联通分量
[题目大意]
- 给定一张图,求最少加入几条边可以使得整个图成为一个边双连通图。
[分析题解]
- 经典的老题目了,学双联通分量必做。前一段时间做了一次,结果不断的WA-_-一怒之下扔掉,今天又回来做,详细的修改了程序框架之后重写很快就过了。
- 具体的说也是非常简单:求双联通分量,染色,缩点,然后求度为1的点数,答案就是(这个数+1) Div 2。
- 因为有重边,以前不太明确怎么处理,直到看了Guo神牛的题解之后,恍然大悟,只需要在Dfs的同时,记下到这个边的这条边的反向边的编号,到时候不要再从这个走回去,但是可以从重边走回去。限制边,而不是限制点,这样处理就搞定了重边的情况。
- 其他的就比较简单了,按照Low和Dfn的定义,如果某个点拓展完后Low=Dfn,那么栈中从栈顶到这个店为止的部分都属于同一个双联通分量。这个由Dfs树的性质决定的。这样求出来之后,只需要对这些点进行染色,不需要真的缩点,只要再去重新枚举原图中的边,向新的颜色不同的顶点之间加边即可。又由于双联通分量的定义,这样新的图一定是一个树,如果不是一个树,那有环的部分按照定义是应该在同一个双联通分量中的。然后求求度数,计算输出即可。
[个人代码]
fayaa.com/code/view/26770/
[相关链接]
- http://www.byvoid.com/blog/biconnect/zh-hans/
- http://www.byvoid.com/blog/pku-3177/
http://www.cnblogs.com/ACystalMoon/articles/2381205.html
- http://www.cnblogs.com/rainydays/archive/2011/07/06/2099524.html
[启发总结]
- 特别是第四个!以前我了解了Low的定义,但是却没有发现这个区别:我们平时使用的Low,都是其子孙通过一条返祖边直接到达的点来决定的。如果我们把这个限制取消掉,按照其提供的做法,可以很简单的对双联通分量进行染色。http://www.cnblogs.com/rainydays/archive/2011/07/06/2099524.htmlpoj3177
求添加多少条边可变连通图。
接着poj3352的看。对于这种题,我们正常的做法是求桥,删桥,求连通分支,缩点,构建新图,求叶子数。
我们有一种简便方法。需要对tarjan算法做一些变化。我们之前规定low[u]是其子孙通过一条返祖边直接到达的点,把这个改成是其子孙可以连续通过多条返祖边所能到达的点。那么low[u]=min(low[v],dfn[u]);
这样做的缺陷是,不能求割点了,多次返祖会导致求割点的错误,在多环两两以单个点相连排成一条线,且每两个连接点间只有一条边的情况中,那些连接点本应是割点,但是在dfs过程中,这些连接点之间的边又恰好不是树枝边的话,low[u]可能会通过多次返祖,从一个割点不断的经过这些割点到达最上边的割点才记录下low[u]。
这样中间的割点就都不符合dfn(u)<=low[v]了。
但是这样做有一个好处,就是所有的对于边的双连通分支都以low标记出来了,即属于同一双连通分支的所有点的low都等于同一个值。因为在不遇到桥的情况下,low可以返祖到该连同分支在遍历树中的最高点(dfn最小的点)。
这样就相当于整理出了所有的对于边的双连通分支。我们直接遍历所有的边,观察边的两端点是否属于同一分支,若不属于则修改两点的度数。然后看有多少个度数为1的点即可。
这题有重边,注意判断。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define maxn 5005
#define maxm 10005
struct Edge
{
int v, next;
} edge[maxm];
int n, m;
int head[maxn];
bool hash[maxn][maxn];
int ecount, tcount;
int dfn[maxn], vis[maxn], low[maxn], degree[maxn];
void addedge(int a, int b)
{
edge[ecount].v = b;
edge[ecount].next = head[a];
head[a] = ecount;
hash[a][b] = hash[b][a] = true;
ecount++;
}
void input()
{
memset(head, -1, sizeof(head));
ecount = 0;
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i++)
{
int a, b;
scanf("%d%d", &a, &b);
a--;
b--;
if (hash[a][b])
continue;
addedge(a, b);
addedge(b, a);
}
}
void dfs(int fa, int u)
{
vis[u] = true;
low[u] = dfn[u] = tcount++;
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if (v == fa)
continue;
if (!vis[v])
dfs(u, v);
low[u] = min(low[u], low[v]);
}
}
int tarjan()
{
memset(dfn, 0, sizeof(dfn));
memset(vis, 0, sizeof(vis));
memset(degree, 0, sizeof(degree));
tcount = 0;
dfs(0, 0);
int ret = 0;
for (int i = 0; i < n; i++)
for (int j = head[i]; j != -1; j = edge[j].next)
{
int v = edge[j].v;
if (low[i] != low[v])
degree[low[i]]++;
}
for (int i = 0; i < n; i++)
if (degree[i] == 1)
ret++;
return (ret + 1) / 2;
}
int main()
{
//freopen("t.txt", "r", stdin);
input();
int ans = tarjan();
printf("%d\n", ans);
return 0;
}
——————————————————————————————————————
你说,我们的存在,永不消逝。对吧?
如果,我们都在努力创造了存在。我们,会幸福的。对吧?