太久不做题,发现很多学过的算法又通通不记得了,或许因为当时既没有深刻理解,也没有及时总结。最近开始复习图论,说是复习,不如说重新学习更加恰当。算法是进入大学紧跟着c语言接触到的第二项“技术”,虽然在竞赛中就好像最终创业失败一样不得善终,总还是希望不辜负那段年轻的时光,也不辜负难得一份坚持了这么久的兴趣。
- 双连通分量


怎么求割点和桥已经比较清晰了,模板用题目测过再拿过来吧。
void DFS(int x){ dfn[x] = low[x] = cnt++; int len = mp[x].size(); for (int i = 0; i < len; i++) { if (dfn[mp[x][i]] == -1) { if (x == root) son ++; DFS(mp[x][i], x); low[x] = min(low[x],low[mp[x][i]]); if (x == root && son > 1 || x != root && dfn[x] <= low[mp[x][i]]) cut[x] ++; } else { low[x] = min(low[x],dfn[mp[x][i]]); } } }
求解点双连通分量:会求割点和桥,求双连通分量只需要再增加一个stack即可,dfs时每访问一条边就将这条边加入stack,当出现割点u(dfn[u]<=low[v])的时候开始退栈,退到边(u,v)为止,退出栈的这些边和点就构成了一个双连通分支。(stack里也可以存点,但是存点要考虑割点会存在多个连通分量这件事,退栈的时候要小心,存边的时候只要把边的两头节点都放在一个连通分量里就ok了)
求边双连通分量:目前没有实际做过,通用的解法是先求出桥,原图删除桥之后就变成了一个一个的连通分量了。
ps.重边的问题:若两点之间有重边,显然这也组成了一个边双连通,在DFS时对重边要特殊处理一下(例如做一个与父节点之间的标记),当然有的问题要求重边也算桥(比如poj 3177)总之要注意是否有重边,重边该怎样处理。
【题目2】poj 2942,Knights of ther Round Table,是一道非常综合的图论题目,核心内容也是双连通分量。这道题目的大意是,n个人,有些人互相憎恨,要剔除一些人使剩下的人围成一个圈,满足:圈中有奇数个人;相邻的人不互相憎恨。注意同一个人可以参加多个会议。
1. 首先,求出原图的补图(由憎恨图变成和谐图,边两端的点表示可以相邻)
2. 对补图求点双连通分量,一个双连通分量里若存在奇圈,那么这个连通分量里的点就都可以参加会议了。因为这个分量里的点会几个一组分布在一个奇圈上,不知道怎么证明,只画了一些图。(比如原来的点分布在一个偶圈上,内部有一条边跟偶数条圈边形成一个奇圈,显然这条边跟剩下的偶数条边也形成了奇圈)相反,双连通分量里若不存在奇圈,那么这个连通分量里的点就都不能参加会议了。(注意点双连通分量里一个点可能存在多个双连通分量中,所以在一个双连通分量的点被删去在另一个双连通分量可能不需要被删去)
3. 判断奇圈用到交叉染色的方法,对一条边的两个点染上不同的颜色,如果能够染成功,就说明没有奇圈。(判断二分图的方法,一个图是二分图当且仅当没有奇圈)
View Code
#include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; const int N = 1005; vector<int>mp[N]; vector<int>cut[N]; int G[N][N],low[N],dfn[N],stack[N],mark[N],color[N]; int m,n,cnt,stack_ptr,cut_num,flag; void DFS(int x, int father) { low[x] = dfn[x] = ++cnt; stack[stack_ptr++] = x; int len = mp[x].size(); for (int i = 0; i < len; i++){ int t = mp[x][i]; if (!dfn[t]){ DFS(t,x); low[x] = min(low[x], low[t]); if (dfn[x] <= low[t]) { cut[cut_num].push_back(x); while (stack[stack_ptr-1] != t) cut[cut_num].push_back(stack[--stack_ptr]); cut[cut_num++].push_back(stack[--stack_ptr]); } } else if (t != father) low[x] = min(low[x], dfn[t]); } } void check(int x, int d, int col,int fa) { color[x] = col; int len = mp[x].size(); for (int i = 0; i < len; i++){ int t = mp[x][i]; if (t == fa) continue; vector<int>::iterator it = find(cut[d].begin(),cut[d].end(),t); if (it != cut[d].end()) if (color[t] == 0) check(t,d,-col,x); else if (color[t] == col) flag = 1; } } int Tarjan() { memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(cut,0,sizeof(cut)); cnt = stack_ptr = cut_num = 0; for (int i = 1; i <= n; i++) if (!dfn[i]) DFS(i,-1); memset(mark,0,sizeof(mark)); for (int i = 0; i < cut_num; i++){ memset(color,0,sizeof(color)); int len = cut[i].size(); flag = 0; if(len >= 3){ check(cut[i][0],i,1,-1); if (flag) for (int j = 0; j < len; j++) mark[cut[i][j]] = 1; } } int ans = 0; for (int i = 1; i <= n; i++) if (!mark[i]) ans++; return ans; } int main() { while (scanf("%d%d",&n,&m) && n+m) { int a,b; for (int i = 0; i <= n; i++){ mp[i].clear(); cut[i].clear(); } memset(G,0,sizeof(G)); while (m--){ scanf("%d%d",&a,&b); G[a][b] = G[b][a] = 1; } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (i!=j && G[i][j]==0) mp[i].push_back(j); printf("%d\n",Tarjan()); } return 0; }

浙公网安备 33010602011771号