现在在外边实训,事情不是很多,赶紧补一下图论,发现自己已经落下太多的东西了啊、、、自己已经越来越差劲了啊,必须让自己多学点啊。。。没带纸笔只好现在博客里记录一下学习的内容,回去之后再好好的整理一下啊。
前几天再跟着老师瞎鼓捣那个所谓的项目,现在项目做了一半了,整体已经差不多也都可以了,所以看看书。
按时间写吧,以后也好整理:
1.10;(总结来自刘汝佳写的大白书)还要感谢老谭的指导。
今天看的东西也不是很多,就是看了一下无向图的割顶和桥:
首先写一下割点的定义:就是在一个图中如果删除掉某些点之后会使得图的连通分量增加,简单的说就是以前是n个回路,先变成n+1,或者是等多的回路了。
最简单的判断一个点是不是割点的方法就是把所有的点进行dfs,然后看是否会有连通分量增加,这样是很慢的,书上说会是O(n*(n+m))的时间复杂度,于是就有了一种更优化的算法。
先提出了时间戳的概念:就是到达这一步的时间是多少,其实并不是真的多少分钟多少秒,而是到达这步是第几步了。
这种算法的简述就是:先求出到达这一步的时间,然后从这个点之后所有子树中(就是说这个点之后所有可以到达这个点的点)求出到达他们之间到达的最少的时间,然后比较最少时间low和pre[u],如果low < pre[u],就是说u点之后的点通过其他的祖先可以比通过u更快的到达,那么这就说明u点之后的某个点不通过u也可以直接到达,那么u点就不是割点了;反之如果low >= pre[u],那就是除了这一点之后的点不能通过其他点直接到达,那么这个点就是割点了啊,因为删掉它之后就会导致连通分量增加。
下面附上通过刘汝佳的代码写的代码,其实主要是老谭同学写的我学习了一下啊。
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
#include <vector>
#include <queue>
#include <map>
using namespace std;
const int maxn = 1000;
vector<int> G[maxn];
int pre[maxn], dfs_clock, low[maxn], n;
bool iscut[maxn];
void init()
{
for(int i=0; i<n; i++) G[i].clear();
memset(iscut, false, sizeof(iscut));
memset(pre, 0, sizeof(pre));
dfs_clock = 0;
}
int dfs(int u, int fa) //u在DFS树中的父结点是fa
{
int lowu = pre[u] = ++dfs_clock;
int child = 0; //子结点数目
for(int i=0; i<G[u].size(); i++)
{
int v = G[u][i];
if(!pre[v]) //没有访问过v, 没有必要用vis标记了
{
child++;
int lowv = dfs(v, u);
lowu = min(lowu, lowv); //用后代的 low 函数更新 u 的 low 函数
if(lowv >= pre[u])
{
iscut[u] = true;
}
}
else if(pre[v] < pre[u] && v != fa) //(u,v)为反向边
{
lowu = min(lowu, pre[v]); //用反向边更新 u 的 low 函数
}
if(fa < 0 && child == 1) iscut[u] = false;
}
low[u] = lowu;
return lowu;
}
int main()
{
int m, u, v;
scanf("%d%d", &n, &m);
init();
for(int i=0; i<m; i++)
{
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=0; i<n; i++)
if(!pre[i])
{
dfs(i, -1);
}
for(int i=0; i<n; i++) //将割点输出
{
if(iscut[i]) printf("%d ", i);
}
putchar('\n');
return 0;
}
实训终于结束了啊,又在那里不幸感冒发了小烧,一直没怎么再看书,所以没写,回来的补上啊。
下面是无向图的点双连通分量:
先摘刘汝佳大白书上的一段话吧:对于一个连通图,如果任意两点至少存在两条“点不重复”的路径,(这里得说一下,一定注意要点不重复,这里是为什么要求出割点的原因,因为割点把它们分成了不同的块)则说明这个图是点联通图,即内部无割顶。
类似的,如果任意两点至少存在两条“边不重复”的路径,我们说这个图是边双连通的。这个要求低一点,只需要每条边至少在一个简单环中,即所有边都不是桥。
对于一张无向图,点-双连通图,点-双连通图的极大子图成为双连通分量或块。每条边恰好属于一个双连通分量,但不同双连通分量可能会有公共点。可以证明不同双连通分量最多只有一个公共点,而且一定是割顶。另一方面,任意割顶都是至少两个不同双连通分量的公共点。
同理边-双连通分量的桥与这里的割顶是一样的啊。
———————————————————————————————————————————————————
在下面说一下我自己的理解: 这里是点连通图,两个点之间要至少有两条路可以到达,而且这两条路上不可以有相同的点,这才是点双连通图。注意了这一点就很明了了啊,每个割点都是一个分割点,把图分成几部分。注意这里的几部分至少有一个是图的块,所以每个块中也至少会包含一个割顶。因此要想找到块就必须先找到割顶,然后根据割顶把图分开,把割顶相连的所有的点加入到栈当中,然后判断所有的点所属于的块集,如果已经属于别的集合的点(一般是割顶),就不加入到容器中,用vector保存点是第几个块的第几个元素。注意当出栈时有可能栈不空但是已经将割顶弹出了,就退出循环,有可能栈不为空。
说明一下:在算法中每个割顶属于哪个块是没有意义的。
以POJ 3352 为例贴一下代码:
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 7704 | Accepted: 3874 |
Description
It's almost summer time, and that means that it's almost summer construction time! This year, the good people who are in charge of the roads on the tropical island paradise of Remote Island would like to repair and upgrade the various roads that lead between the various tourist attractions on the island.
The roads themselves are also rather interesting. Due to the strange customs of the island, the roads are arranged so that they never meet at intersections, but rather pass over or under each other using bridges and tunnels. In this way, each road runs between two specific tourist attractions, so that the tourists do not become irreparably lost.
Unfortunately, given the nature of the repairs and upgrades needed on each road, when the construction company works on a particular road, it is unusable in either direction. This could cause a problem if it becomes impossible to travel between two tourist attractions, even if the construction company works on only one road at any particular time.
So, the Road Department of Remote Island has decided to call upon your consulting services to help remedy this problem. It has been decided that new roads will have to be built between the various attractions in such a way that in the final configuration, if any one road is undergoing construction, it would still be possible to travel between any two tourist attractions using the remaining roads. Your task is to find the minimum number of new roads necessary.
Input
The first line of input will consist of positive integers n and r, separated by a space, where 3 ≤ n ≤ 1000 is the number of tourist attractions on the island, and 2 ≤ r ≤ 1000 is the number of roads. The tourist attractions are conveniently labelled from 1 to n. Each of the following r lines will consist of two integers, v and w, separated by a space, indicating that a road exists between the attractions labelled v and w. Note that you may travel in either direction down each road, and any pair of tourist attractions will have at most one road directly between them. Also, you are assured that in the current configuration, it is possible to travel between any two tourist attractions.
Output
One line, consisting of an integer, which gives the minimum number of roads that we need to add.
Sample Input
Sample Input 1 10 12 1 2 1 3 1 4 2 5 2 6 5 6 3 7 3 8 7 8 4 9 4 10 9 10 Sample Input 2 3 3 1 2 2 3 1 3
Sample Output
Output for Sample Input 1 2 Output for Sample Input 2 0题目的大意就是:
在暑假里景区要修路,为保障游客可以正常的游览,所以要修的路不能是“桥”,否则回使图不连通了,让你求出这个图再修之前至少先加入多少个点,使得这个图可以保证这个图是点-双连通的。
解体的思路是:求出所有的块,然后求出所有“块”中只有一个割点的数目然后[cnt+1]/2就是要修的路的数目。
#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <iomanip>
#include <stdio.h>
#include <string>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#define eps 1e-7
#define M 1001000
#define LL __int64
#define INF 0x3f3f3f3f
#define PI 3.1415926535898
using namespace std;
const int maxn = 1001000;
int pre[maxn], iscut[maxn], bccno[maxn], dfs_clock, bcc_cnt, n;
vector<int> G[maxn], bcc[maxn];
struct Edge
{
int u, v;
};
stack<Edge> S;
int dfs(int u, int fa)
{
int lowu = pre[u] = ++dfs_clock;
int child = 0;
for(int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
Edge e;
e.u = u;
e.v = v;
if(!pre[v])
{
S.push(e);
child ++;
int lowv = dfs(v, u);
lowu = min(lowv, lowu);
if(lowv >= pre[u])
{
iscut[u] = 1;
bcc_cnt ++;
bcc[bcc_cnt].clear();
while(1)
{
Edge x = S.top();
S.pop();
if(bccno[x.u] != bcc_cnt)
{
bcc[bcc_cnt].push_back(x.u);
bccno[x.u] = bcc_cnt;
}
if(bccno[x.v] != bcc_cnt)
{
bcc[bcc_cnt].push_back(x.v);
bccno[x.v] = bcc_cnt;
}
if(x.v == v && x.u == u)
break;
}
}
}
else if(pre[v] < pre[u] && v != fa)
{
S.push(e);
lowu = min(lowu, pre[v]);
}
}
if(fa < 0 && child == 1)
iscut[u] = 0;
return lowu;
}
void find_bcc()
{
int i;
memset(bccno , 0 , sizeof(bccno));
memset(iscut , 0 , sizeof(iscut));
memset(pre , 0 , sizeof(pre));
bcc_cnt = 0;
dfs_clock = 0;
for(i = 1; i <= n; i++)
if(!pre[i])
dfs(i, -1);
}
int main()
{
int u, v, m, i, j;
while(cin >>n>>m)
{
for(i = 1; i <= m; i++)
{
cin >>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
find_bcc();
//cout <<bcc_cnt<<endl;
int cnt = 0;
int ant = 0;
for(i = 1; i <= bcc_cnt; i++)
{
ant = 0;
for(j = 0; j < bcc[i].size(); j++)
{
if(iscut[bcc[i][j]])
ant ++;
}
if(ant == 1)
cnt++;
}
cout<<(cnt+1)/2<<endl;
}
return 0;
}
未完待续。。。。。