藍橋杯歷屆試題 危險係數(dfs或者並查集求無向圖關於兩點的割點個數)
阿新 • • 發佈:2019-01-04
Description
抗日戰爭時期,冀中平原的地道戰曾發揮重要作用。地道的多個站點間有通道連線,形成了龐大的網路。但也有隱患,當敵人發現了某個站點後,其它站點間可能因此會失去聯絡。
我們來定義一個危險係數DF(x,y):
對於兩個站點x和y (x != y), 如果能找到一個站點z,當z被敵人破壞後,x和y不連通,那麼我們稱z為關於x,y的關鍵點。相應的,對於任意一對站點x和y,危險係數DF(x,y)就表示為這兩點之間的關鍵點個數。
本題的任務是:已知網路結構,求兩站點之間的危險係數。
Input
輸入資料第一行包含2個整數n(2 <= n <= 1000), m(0 <= m <= 2000),分別代表站點數,通道數;接下來m行,每行兩個整數 u,v (1 <= u, v <= n; u != v)代表一條通道;
最後1行,兩個數u,v,代表詢問兩點之間的危險係數DF(u, v)。
Output
Sample Input
7 6 1 3 2 3 3 4 3 5 4 5 5 6 1 6
Sample Output
2
Source
藍橋杯 解法1:DFS 求關於兩個點的割點dfs爆搜,找到這兩點間的所有路徑數目ans,如果在1這些路徑中,某些點出現的次數是ans
那麼該點一定是關於目標兩點的割點 具體做法:
搜尋路徑,記錄路徑中每個結點的出現次數,最後與可行路徑數比較 將目標兩點設定為起點s和終點f,dfs爆搜其路徑
邊搜邊記錄其路徑,搜到終點之後,將記錄的路徑的結點出現次數加1
如果在全部搜尋完之後,有點在所有可行路徑中出現的次數等於可行路徑數,那麼該點是關於起點和終點和割點
#include<stdio.h> #include<iostream> #include<math.h> #include<string.h> #include<set> #include<map> #include<list> #include<queue> #include<algorithm> using namespace std; typedef long long LL; int mon1[13]= {0,31,28,31,30,31,30,31,31,30,31,30,31}; int mon2[13]= {0,31,29,31,30,31,30,31,31,30,31,30,31}; int dir[4][2]= {{0,1},{0,-1},{1,0},{-1,0}}; int getval() { int ret(0); char c; while((c=getchar())==' '||c=='\n'||c=='\r'); ret=c-'0'; while((c=getchar())!=' '&&c!='\n'&&c!='\r') ret=ret*10+c-'0'; return ret; } #define max_v 1005 int vis[max_v]; int cnt[max_v]; int path[max_v]; int G[max_v][max_v]; int n,m; int ans; int s,f; void init() { memset(vis,0,sizeof(vis)); memset(cnt,0,sizeof(cnt)); memset(path,0,sizeof(path)); memset(G,0,sizeof(G)); ans=0; } void dfs(int cur,int len) { if(cur==f) { ans++; for(int i=1;i<len;i++) cnt[path[i]]++; return ; } for(int i=1;i<=n;i++) { if(G[cur][i]&&vis[i]==0) { vis[i]=1; path[len]=i; dfs(i,len+1); vis[i]=0; } } } int main() { int x,y; scanf("%d %d",&n,&m); init(); for(int i=1;i<=m;i++) { scanf("%d %d",&x,&y); G[x][y]=G[y][x]=1; } scanf("%d %d",&s,&f); vis[s]=1; path[1]=s; dfs(s,2); if(ans==0) { printf("-1\n"); return 0; } int sum=0; for(int i=1;i<=n;i++) if(cnt[i]==ans) sum++; printf("%d\n",sum-2); return 0; } /* 求關於兩個點的割點 dfs爆搜,找到這兩點間的所有路徑數目ans,如果在1這些路徑中,某些點出現的次數是ans 那麼該點一定是關於目標兩點的割點 具體做法: 搜尋路徑,記錄路徑中每個結點的出現次數,最後與可行路徑數比較 將目標兩點設定為起點s和終點f,dfs爆搜其路徑 邊搜邊記錄其路徑,搜到終點之後,將記錄的路徑的結點出現次數加1 如果在全部搜尋完之後,有點在所有可行路徑中出現的次數等於可行路徑數,那麼該點是關於起點和終點和割點 */
解法二:並查集
無向圖求關於某兩點的割點個數
遍歷每個點,進行多次並查集操作,如果去除和某個點有關的所有邊
導致起點和終點不能連通,那麼該點屬於關於這兩點的割點
該方法屬於暴力法
#include<stdio.h> #include<iostream> #include<math.h> #include<string.h> #include<set> #include<map> #include<list> #include<queue> #include<algorithm> using namespace std; typedef long long LL; int mon1[13]= {0,31,28,31,30,31,30,31,31,30,31,30,31}; int mon2[13]= {0,31,29,31,30,31,30,31,31,30,31,30,31}; int dir[4][2]= {{0,1},{0,-1},{1,0},{-1,0}}; int getval() { int ret(0); char c; while((c=getchar())==' '||c=='\n'||c=='\r'); ret=c-'0'; while((c=getchar())!=' '&&c!='\n'&&c!='\r') ret=ret*10+c-'0'; return ret; } #define max_v 1005 int pa[max_v]; int n,m; int a[max_v],b[max_v]; int s,f; void init() { for(int i=1;i<=n;i++) pa[i]=i; } int find_set(int x) { if(x!=pa[x]) pa[x]=find_set(pa[x]); return pa[x]; } void union_set(int x,int y) { x=find_set(x); y=find_set(y); if(x==y) return ; pa[x]=y; } int fun(int v) { init(); for(int i=1;i<=m;i++) { if(a[i]==v||b[i]==v) continue; union_set(a[i],b[i]); } if(find_set(s)!=find_set(f)) return 0; else return 1; } int main() { int x,y; scanf("%d %d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d %d",&x,&y); a[i]=x; b[i]=y; } scanf("%d %d",&s,&f); int sum=0; for(int i=1;i<=n;i++) { if(i==s||i==f) continue; if(fun(i)==0) sum++; } printf("%d\n",sum); return 0; } /* 無向圖求關於某兩點的割點個數 遍歷每個點,進行多次並查集操作,如果去除和某個點有關的所有邊 導致起點和終點不能連通,那麼該點屬於關於這兩點的割點 該方法屬於暴力法 */