HDU 3313 Key Vertex

题意:给出一张有向图。如果一个节点在图中被删除后,就能使得从S->T不连通,则称这个点key vertex。问这张图中有多少个点是key vertex。

网上有两种说法,一种是网络流,就是拆点建图,求最小割,为了限制增广次数,把S、T拆点后的容量限制到2,这样一来跑最大流得到maxflow,如果maxflow=0,则说明全部的点都可以是key vertex,如果maxflow=2,则说明只有S、T是key vertex,如果maxflow=1的话,就需要到残量网络中去找了。怎么找呢?首先从S出发始寻找在残量网络中可以到达的所有的点,把它们保存在一个队列里,并记上vis标记,然后把这个队列里的点u,我们判断从u出发的所有已经满流的正向边指向的点是否在BFS的时候被访问过,如果没有被访问过,那这个点就是一个key vertex,找到一个key vertex以后就可以退出这轮循环了。然后以刚才找到的key vertex为src再做BFS,这样一直迭代下去,直到找到了一个为汇点T的key vertex。

另外一种说法比网络流好写多了,先找一条最短路,并记录路径。然后也是从S开始找,找一个距离S最远的且在最短路路径上的点K,这个K就是一个key vertex。然后继续,以K为src去找一个距离K最远的且在最短路路径上的点K',直到找到T为止。

贴一下网络流写的代码:

  1 #pragma comment(linker, "/STACK:1024000000,1024000000")
  2 #include <iostream>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <algorithm>
  6 #define INF 1<<30
  7 #define maxn 200010
  8 #define maxm 1000000
  9 using namespace std;
 10 
 11 int v[maxm],next[maxm],w[maxm];
 12 int first[maxn],d[maxn],work[maxn],q[maxn],vis[maxn];
 13 int e,S,T,n,m,rear,cnt;
 14 
 15 void init(){
 16     e = 0;
 17     memset(first,-1,sizeof(first));
 18 }
 19 
 20 void add_edge(int a,int b,int c){
 21     v[e] = b;next[e] = first[a];w[e] = c;first[a] = e++;
 22     v[e] = a;next[e] = first[b];w[e] = 0;first[b] = e++;
 23 }
 24 
 25 int bfs(){
 26     rear = 0;
 27     memset(d,-1,sizeof(d));
 28     d[S] = 0;q[rear++] = S;
 29     for(int i = 0;i < rear;i++){
 30         for(int j = first[q[i]];j != -1;j = next[j])
 31             if(w[j] && d[v[j]] == -1){
 32                 d[v[j]] = d[q[i]] + 1;
 33                 q[rear++] = v[j];
 34                 if(v[j] == T)   return 1;
 35             }
 36     }
 37     return 0;
 38 }
 39 
 40 int dfs(int cur,int a){
 41     if(cur == T)    return a;
 42     for(int &i = work[cur];i != -1;i = next[i]){
 43         if(w[i] && d[v[i]] == d[cur] + 1)
 44             if(int t = dfs(v[i],min(a,w[i]))){
 45                 w[i] -= t;w[i^1] += t;
 46                 return t;
 47             }
 48     }
 49     return 0;
 50 }
 51 
 52 int dinic(){
 53     int ans = 0;
 54     while(bfs()){
 55         memcpy(work,first,sizeof(first));
 56         while(int t = dfs(S,INF))   ans += t;
 57     }
 58     return ans;
 59 }
 60 
 61 void bfs(int src){
 62     rear = 0;
 63     vis[src] = 1,q[rear++] = src;
 64     for(int i = 0;i < rear;i++){
 65         for(int j = first[q[i]];j != -1;j = next[j])
 66             if(w[j] && !vis[v[j]]){
 67                 vis[v[j]] = 1;
 68                 q[rear++] = v[j];
 69             }
 70     }
 71 }
 72 
 73 void solve(){
 74     memset(vis,0,sizeof(vis));
 75     w[first[S]] = 0,w[first[T-n]] = 0;
 76     int src = S;
 77     bool flag = 1;
 78     while(1){
 79         bfs(src);
 80         flag = 1;
 81         for(int i = 0;i < rear;i++){
 82             if(!flag)   break;
 83             for(int j = first[q[i]];j != -1;j = next[j]){
 84                 if((!(j&1)) && (!w[j]) && (!vis[v[j]])){
 85                     cnt++;
 86                     src = v[j];
 87                     flag = 0;
 88                     if(src == T)    return;
 89                 }
 90             }
 91         }
 92     }
 93 }
 94 int main()
 95 {
 96     while(scanf("%d%d",&n,&m) == 2){
 97         init();
 98         for(int i = 0;i < m;i++){
 99             int a,b;
100             scanf("%d%d",&a,&b);
101             add_edge(a+n,b,INF);
102         }
103         scanf("%d%d",&S,&T);
104         for(int i = 0;i < n;i++){
105             if(i != S && i != T)    add_edge(i,i+n,1);
106             else    add_edge(i,i+n,2);
107         }
108         T += n;
109         int maxflow = dinic();
110         if(maxflow == 2){
111             printf("2\n");
112         }else if(maxflow == 0){
113             printf("%d\n",n);
114         }else{
115             cnt = 0;
116             solve();
117             printf("%d\n",cnt);
118         }
119     }
120     return 0;
121 }
View Code

 

转载于:https://www.cnblogs.com/zhexipinnong/p/3397548.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值