日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

割点

發布時間:2024/9/30 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 割点 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題目:求一個連通圖的割點,割點的定義是,如果除去此節點和與其相關的邊,圖不再連通,描述算法。

分析:

1. 最簡單也是最直接的算法是,刪除一個點然后判斷連通性,如果刪除此點,圖不再連通,則此點是割點,反之不是割點(圖的連通性一般通過深搜來判定,是否能一次搜索完 全部頂點);

2. 通過深搜優先生成樹來判定。從任一點出發深度優先遍歷得到優先生成樹,對于樹中任一頂點V而言,其孩子節點為鄰接點。由深度優先生成樹可得出兩類割點的特性:

???? (1)若生成樹的根有兩棵或兩棵以上的子樹,則此根頂點必為割點。因為圖中不存在連接不同子樹頂點的邊,若刪除此節點,則樹便成為森林;

???? (2)若生成樹中某個非葉子頂點V,其某棵子樹的根和子樹中的其他節點均沒有指向V的祖先的回邊,則V為割點。因為刪去v,則其子樹和圖的其它部分被分割開來。

仍然利用深搜算法,只不過在這里定義visited[v]表示為深度優先搜索遍歷圖時訪問頂點v的次序號,定義low[v]=Min{visited[v],low[w],visited[k]},其中w是頂點v在深度優先生成樹上的孩子節點;k是頂點v在深度優先生成樹上由回邊聯結的祖先節點。

?? 割點判定條件:如果對于某個頂點v,存在孩子節點w且low[w]>=visited[v],則該頂點v必為關節點。因為當w是v的孩子節點時,low[w]>=visited[v],表明w及其子孫均無指向v的祖先的回邊,那么當刪除頂點v后,v的孩子節點將于其他節點被分割開來,從來形成新的連通分量。

#include <iostream> #include <string> using namespace std;#define MAX_VERTEX_NUM 13//鄰接表存儲結構 typedef struct ArcNode{int adjvex;ArcNode *nextarc; }ArcNode;typedef struct VNode{string data;ArcNode* firstarc; }VNode,AdjList[MAX_VERTEX_NUM];typedef struct{AdjList vertices;int vexnum, arcnum; }ALGraph;//返回u在圖中的位置 int LocateVex(ALGraph G, string u) {for(int i=0; i<G.vexnum; i++)if(G.vertices[i].data==u)return i;return -1; }//構造圖 void CreateDG(ALGraph &G) {string v1, v2;int i, j, k;cout<<"請輸入頂點數和邊數:";cin>>G.vexnum>>G.arcnum;cout<<"請輸入頂點:";for(i=0; i<G.vexnum; i++){cin>>G.vertices[i].data;G.vertices[i].firstarc=NULL;}cout<<"請輸入邊:"<<endl;for(k=0; k<G.arcnum; k++){cin>>v1>>v2;i=LocateVex(G, v1);j=LocateVex(G, v2);//無向圖ArcNode *arc=new ArcNode;arc->adjvex=j;arc->nextarc=G.vertices[i].firstarc;G.vertices[i].firstarc=arc;arc=new ArcNode;arc->adjvex=i;arc->nextarc=G.vertices[j].firstarc;G.vertices[j].firstarc=arc;}}//求割點 int count ; int visited[MAX_VERTEX_NUM]; int low[MAX_VERTEX_NUM];//從第v0個頂點出發深搜,查找并輸出關節點(割點) void DFSArticul(ALGraph G, int v0) {int min, w;ArcNode *p;visited[v0]=min=++count;//v0是第count個訪問的頂點,min的初值為visited[v0],即v0的訪問次序for(p=G.vertices[v0].firstarc; p ; p=p->nextarc){w=p->adjvex;if(visited[w]==0)//w未曾訪問,是v0的孩子{DFSArticul(G, w);//從第w個頂點出發深搜,查找并輸出關節點(割點),返回前求得low[w]if(low[w]<min)//如果v0的孩子節點w的low[]小,說明孩子節點還與其他節點(祖先)相鄰min=low[w];if(low[w]>=visited[v0])//v0的孩子節點w只與v0相連,則v0是關節點(割點)cout<<G.vertices[v0].data<<" ";}else if(visited[w]<min)//w已訪問,則w是v0生成樹上祖先,它的訪問順序必小于minmin=visited[w];}low[v0]=min;//low[v0]取三者最小值}void FindArticul(ALGraph G) {int i, v;ArcNode *p;count=1;visited[0]=1;//從0號節點開始for(i=1; i<G.vexnum; i++)visited[i]=0;p=G.vertices[0].firstarc;v=p->adjvex;DFSArticul(G, v);if(count<G.vexnum){cout<<G.vertices[0].data<<" ";while(p->nextarc){p=p->nextarc;v=p->adjvex;if(visited[v]==0)DFSArticul(G, v);}} }void main() {ALGraph g;CreateDG(g);cout<<"割點如下: "<<endl;FindArticul(g);cout<<endl; }



這篇博客講解的更為詳細:http://blog.csdn.net/xinghongduo/article/details/6202646

黑書上給出了關于求點割集的算法,但是比較模糊,我查閱了網絡上的相關資料,理解了求點割集的過程,寫出如下求點割集的代碼,并寫了一些簡單的證明.

?

割點集的定義:如果在連通圖G中去掉某一點后圖不連通,那么這個點即為G的割點,所有割點的集合即為點割集。

?

求點割集的方法:利用tarjan算法的思想,用數組dfn[v]存儲DFS遍歷到點v的時間,數組low[v]存儲點v能追溯到最早的祖先節點。

如果對于點v來說有如下結論:

?

?

1.如果點v是DFS序列的根節點,則如果v有一個以上的孩子,則v是一個割點。

2.如果v不是DFS序列根節點,并且點v的任意后繼u能追溯到最早的祖先節點low[u]>=dfn[v],則v是一個割點。

?

?

證明1:

??

?????? 假設DFS遍歷的第一個節點v不是割點,那么則有low[v]=dfn[v]=1,繼續對v的孩子節點u遍歷,必然有low[u]>=dfn[v],按照第二條性質,則v是割點,但我們已經假設v不是割點。這是由于v是DFS遍歷的起始節點,在遍歷序列中v沒有祖先節點,v的所有后繼節點能追溯到最早的祖先節點最多也就是v了,不可能比v再早了,因此必須把DFS遍歷的第一個節點v單獨考慮,那么怎么判斷v是不是割點呢?

??????例如上圖,設v是DFS序列訪問的第一個節點,對v的孩子節點u和u的所有孩子節點進行DFS遍歷并標記為已經訪問后,如果v的另一個孩子節點k沒有被標記為已經訪問,那么u和k之間一定不存在邊,也就是說u和k之間的連通必然需要點v,因此如果v是DFS遍歷的第一個節點,對v是否為割點的判斷方法是:看v是不是有多個孩子,如果有則v是割點。

證明2:

????? 如果v不是DFS遍歷的第一個節點,那么對于v的所有后繼節點來說,如果v不是割點(也就是如果刪掉點v,剩下的圖還是連通圖),那么v的后繼節點必然能追溯到DFS遍歷序列中v的祖先節點,也就是v的后繼節點中存在到達DFS序列中v的祖先的路徑,因此當DFS回溯到v節點時對于v的所有后繼節點u來說,都有low[u]<dfn[v]。

????? 如果v是一個割點,對所有v的后繼節點u進行DFS后,必然有low[u]>=dfn[v],這是因為,當遍歷v并將其鎖定后,到達v的祖先節點的路徑已經被封死,v的后繼節點必然不可能訪問到v的祖先節點,因此,必然有low[u]>=dfn[v]。

?

?

有了上面的分析,下面寫出求無向圖點割集的代碼:

#include<iostream> using namespace std;struct L {int v; L *next; };class HEAD { public:int id; L *next;HEAD(){ id=0; next=NULL;} };HEAD head[1000]; int dfn[1000],low[1000],t; bool lock[1000],C[1000];void find(int father,int v) {int count=0; /*統計v的孩子數*/dfn[v]=low[v]=++t; /*將訪問時間賦給dfn[v]和low[v]*/lock[v]=false; /*標記v點已經訪問過,不能再被訪問*/for(L *p=head[v].next;p!=NULL;p=p->next){if(lock[p->v]) /*如果v的直接后繼節點沒有訪問過,則對其遍歷*/{find(v,p->v); /*對v的直接后繼遍歷*/count++; /* 孩子數+1 */if(low[v]>low[p->v]) /*如果v的孩子能追溯到更早的祖先,則v也能追溯到*/low[v]=low[p->v];}else if(p->v!=father&&low[p->v]<low[v]) /*如果v的直接孩子節點已經被訪問過*/low[v]=low[p->v];if(!father&&count>1) /*如果當前節點是DFS遍歷到的第一個節點,則判斷其是否有多個孩子*/C[v]=true;else if(father&&dfn[v]<=low[p->v]) /*否則判斷其后繼能否追溯到v的祖先*/C[v]=true;} }int main() {int n,i,a,b;cin>>n;while(cin>>a>>b&&a&&b) /*建立鄰接表,輸入無向圖邊每條a b,以0 0結束*/{L *p=new L;p->next=head[b].next;head[b].next=p;p->v=a;p=new L;p->next=head[a].next;head[a].next=p;p->v=b;head[b].id++;head[a].id++;}memset(lock,true,sizeof(lock));memset(dfn,0,sizeof(int)*1000);memset(C,0,sizeof(C)); /*C數組用來標記那些點是割點,剛開始全部置為false*/t=0; /*訪問時間*/find(0,1);/*開始對1號點DFS,第一個遍歷的前驅節點設為0*/for(i=1;i<=n;i++) /*輸入割點*/if(C[i])cout<<i<<' ';cout<<endl;}




總結

以上是生活随笔為你收集整理的割点的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。