混合欧拉回路 网络流 dinic 以及sap 算法

 

poj 1637 混合欧拉回路 网络流 dinic 以及sap 算法

分类: 网络流   395人阅读  评论(0)  收藏  举报

题意:给出一个混合图(有的边有向,有的边无向),问此图是否存在欧拉回路。

先说说欧拉回路吧,起点和终点相同,经过图G的每条边一次,且只经过一次的路径称为欧拉回路。
按照图的不同分为:无向图欧拉回路、有向图欧拉回路和混合图欧拉回路。

判断一个图是否存在欧拉回路:
1.无向图:图连通,且图中均为偶度顶点。
2.有向图:图连通,且图中所有顶点出入度相等。
3.混合图:混合图欧拉回路的判断是用网络流,实现方法:

首先对所有的无向边随便定向,之后会进行调整。然后统计每个点的出入度,如果有某个点出入度之差为奇数,则不存在欧拉回路,因为相差为奇数的话,无论如果调整边,都不能使得每个点的出入度相等。

现在每个点的出入度之差为偶数了,把这个偶数除以2,得x。则对每个顶点改变与之相连的x条边的方向就可以使得该点出入度相等。如果每个点都能达到出入度相等,自然就存在欧拉回路了。

现在问题就变成了改变哪些边的方向能让每个点出入度相等了,构造网络流模型。
有向边不能改变方向,所以不添加有向边。对于在开始的时候任意定向的无向边,按所定的方向加边,容量为1。源点向所有出>入的点连边,容量为该点的x值;所有入>出的点向汇点连边,容量为该点的x值。

建图完成了,求解最大流,如果能满流分配,则存在欧拉回路。那么哪些边改变方向才能得到欧拉回路呢?查看流量分配,所有流量非0的边就是要改变方向的边。

原理是因为满流分配,所以和源点相连的点一定都有x条边流入,将这些边反向这些点就出入度相等了,和汇点相连的亦然。没有和源、汇相连的已经出入度相等了,当然不用修改,至此欧拉回路求解完毕。

 

以上来自   http://zhyu.me/acm/zoj-1992-and-poj-1637.html

 

我个人的理解:  首先 上面红色的   是由于  在欧拉回路中每个点的出度都等于入度。 那么当一个点的出度增加1 那么入度一定减少1 那么他们的差依旧保持为偶数 如果不是 说明这个图不会是欧拉回路

 

我们把无向边随意变成有向边后    会存在有些点的入度不等于出度  ,那么我们需要改变以前的那些无向边中的部分边  使得每个点都能够出度等于入度。

 如何修改那 ?   按照上面的方式连接各个边之后    形成的图的意思 就是: 从s到所有边中找出部分边修改使得与s相连的每个点的入度等于出度 同时使得与t相连的每个点的出度等于入度   

DINIC算法

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define VM 222  
  4. #define EM 20550  
  5. #define inf 0x3f3f3f3f  
  6. struct Edge  
  7. {  
  8.     int frm,to,cap,next;  
  9. }edge[EM];  
  10.   
  11. int head[VM],dep[VM],ep;     //dep为点的层次  
  12. void addedge (int cu,int cv,int cw)  //第一条边下标必须为偶数  
  13. {  
  14.     edge[ep].frm = cu;  
  15.     edge[ep].to = cv;  
  16.     edge[ep].cap = cw;  
  17.     edge[ep].next = head[cu];  
  18.     head[cu] = ep;  
  19.     ep ++;  
  20.     edge[ep].frm = cv;  
  21.     edge[ep].to = cu;  
  22.     edge[ep].cap = 0;  
  23.     edge[ep].next = head[cv];  
  24.     head[cv] = ep;  
  25.     ep ++;  
  26. }  
  27.   
  28. int BFS (int src,int des)     //求出层次图  
  29. {  
  30.     int que[VM],i,front = 0,rear = 0;  
  31.     memset (dep,-1,sizeof(dep));  
  32.     que[rear++] = src;  
  33.     dep[src] = 0;  
  34.     while (front != rear)  
  35.     {  
  36.         int u = que[front++];  
  37.         front = front%VM;  
  38.         for (i = head[u];i != -1;i = edge[i].next)  
  39.         {  
  40.             int v = edge[i].to;  
  41.             if (edge[i].cap > 0&&dep[v] == -1) //容量大于0&&未在dep中  
  42.             {  
  43.                 dep[v] = dep[u] + 1;        //建立层次图  
  44.                 que[rear ++] = v;  
  45.                 rear = rear % VM;  
  46.                 if (v == des)  //找到汇点 返回  
  47.                     return 1;  
  48.             }  
  49.         }  
  50.     }  
  51.     return 0;  
  52. }  
  53. int dinic (int src,int des)  
  54. {  
  55.     int i,res = 0,top;  
  56.     int stack[VM];    //stack为栈,存储当前增广路  
  57.     int cur[VM];        //存储当前点的后继 跟head是一样的  
  58.     while (BFS(src,des))   //if BFS找到增广路  
  59.     {  
  60.         memcpy (cur,head,sizeof (head));  
  61.         int u = src;       //u为当前结点  
  62.         top = 0;  
  63.         while (1)  
  64.         {  
  65.             if (u == des)     //增广路已全部进栈  
  66.             {  
  67.                 int min = inf,loc ;  
  68.                 for (i = 0;i < top;i ++)       //找最小的增广跟并loc记录其在stack中位置  
  69.                     if (min > edge[stack[i]].cap)  //以便退回该边继续DFS  
  70.                     {  
  71.                         min = edge[stack[i]].cap;  
  72.                         loc = i;  
  73.                     }  
  74.                 for (i = 0;i < top;i ++)   //偶数^1 相当加1 奇数^1相当减1 当正向边 = 0&&路径不合适时,正加负减  
  75.                 {                           //偶数是正向边,奇数是负向边,边从0开始  
  76.                     edge[stack[i]].cap -= min;  
  77.                     edge[stack[i]^1].cap += min;  
  78.                 }                              //将增广路中的所有边修改  
  79.                 res += min;  
  80.                 top = loc;  
  81.                 u = edge[stack[top]].frm;         //当前结点修改为最小边的起点  
  82.             }  
  83.             for (i = cur[u];i != -1;cur[u] = i = edge[i].next)   //找到当前结点对应的下一条边  
  84.                 if (edge[i].cap != 0&&dep[u] + 1 == dep[edge[i].to])//不满足条件时,修改cur值(去掉不合适的占)eg:1-->2 1-->3 1-->4 有边 但只有  
  85.                     break;                                  // 1-->4 这条边满足条件 就把1到2、3的边给去掉  
  86.             if (cur[u] != -1)            //当前结点的下一条边存在  
  87.             {  
  88.                 stack[top ++] = cur[u];   //把该边放入栈中  
  89.                 u = edge[cur[u]].to;         //再从下个点开始找  
  90.             }  
  91.             else  
  92.             {  
  93.                 if (top == 0)        //当前结点无未遍历的下一条边且栈空,DFS找不到下一条增广路  
  94.                     break;  
  95.                 dep[u] = -1;            //当前结点不在增广路中,剔除该点  
  96.                 u = edge[stack[--top]].frm; //退栈 回朔,继续查找  
  97.             }  
  98.         }  
  99.     }  
  100.     return res;  
  101. }  
  102.   
  103. int in[VM],out[VM];   
  104.   
  105. int abs(int qa)  
  106. {  
  107.     if(qa>0) return qa;  
  108.     return -qa;  
  109. }  
  110. int main()   
  111. {   
  112.     int T,n,m;   
  113.     int u,v,d;   
  114.     scanf("%d",&T);   
  115.     while(T--)   
  116.     {   
  117.         scanf("%d%d",&n,&m);   
  118.         ep=0;  
  119.         memset (head,-1,sizeof(head));  
  120.         memset(out,0,sizeof(out));  
  121.         memset(in,0,sizeof(in));  
  122.         while(m--)   
  123.         {   
  124.             scanf("%d%d%d",&u,&v,&d);   
  125.             if(u == v)   
  126.                 continue;   
  127.             out[u]++;   
  128.             in[v]++;   
  129.             if(!d)   
  130.             {   
  131.                 addedge(u,v,1);   
  132.             }   
  133.         }   
  134.         int flag=0;   
  135.         int sum=0;   
  136.         for(int i=1;i<=n;i++)   
  137.         {   
  138.             if(abs(in[i]-out[i])%2 == 1)   
  139.             {   
  140.                 flag=1;   
  141.                 break;   
  142.             }   
  143.             if(in[i]<out[i])   
  144.             {   
  145.                 addedge(0,i,(out[i]-in[i])/2);   
  146.                 sum += (out[i]-in[i])/2;   
  147.             }   
  148.             else    
  149.             {   
  150.                 addedge(i,n+1,(in[i]-out[i])/2);   
  151.             }   
  152.         }   
  153.         if(flag)     
  154.         {     
  155.             printf("impossible\n");     
  156.             continue;     
  157.         }     
  158.         int ans = dinic(0,n+1);   
  159.         if(sum == ans)   
  160.             printf("possible\n");   
  161.         else    
  162.             printf("impossible\n");   
  163.     }   
  164.     return 0;   
  165. }   


 

 

sap算法 可以做模板

 

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include<string.h>  
  3. #include<algorithm>  
  4. using namespace std;   
  5. #define MAX 210   
  6. #define INFINITY 1000000000   
  7. struct edge   
  8. {   
  9.     int cap;   
  10.     int flow;   
  11.     int ver;   
  12.     edge *next;   
  13.     edge *rev;   
  14. };   
  15. edge edges[2500];   
  16. edge *link[MAX+1];   
  17. int dist[MAX +1];   
  18. int h[MAX + 1];   
  19. int in[MAX+1];   
  20. int out[MAX+1];   
  21.    
  22. int num;   
  23. int total_flow;   
  24. int min(int a, int b)   
  25. {   
  26.     return a < b ? a : b;   
  27. };   
  28. void addedge(int start, int end, int c)   
  29. {   
  30.     num++;   
  31.     edges[num].ver = end;   
  32.     edges[num].cap = c;   
  33.     edges[num].next = link[start];   
  34.     link[start] = edges + num;   
  35.     num++;   
  36.     edges[num].ver = start;   
  37.     edges[num].cap = 0;   
  38.     edges[num].next = link[end];   
  39.     link[end] = edges + num;   
  40.     link[start]->rev = link[end];   
  41.     link[end]->rev = link[start];   
  42. }   
  43. void rev_bfs(int n, int src, int des)   
  44. {   
  45.     int q[MAX + 1];   
  46.     int head = 0;   
  47.     int tail = 0;   
  48.     for(int i = 1; i <= n; i++)   
  49.     {   
  50.         dist[i] = MAX;   
  51.         h[i] = 0;   
  52.     }   
  53.     q[tail++] = des;   
  54.     dist[des] = 0;   
  55.     h[0] = 1;   
  56.     int p;   
  57.     while(tail != head)   
  58.     {   
  59.         p = q[head++];   
  60.         for(edge *e = link[p]; e; e = e->next)   
  61.         {   
  62.             if(dist[e->ver] != MAX || e->rev->cap == 0)   
  63.                 continue;   
  64.             dist[e->ver] = dist[p] + 1;   
  65.             h[dist[e->ver]]++;   
  66.             q[tail++] = e->ver;   
  67.         }   
  68.     }   
  69. }   
  70.    
  71. int sap(int n, int src, int des)   
  72. {   
  73.     rev_bfs(n, src, des);   
  74.     edge *cur_edges[MAX+1];   
  75.     edge *rev_path[MAX+1];   
  76.     total_flow = 0;   
  77.     int i;   
  78.     for(i = 1; i <= n ; i++)   
  79.         cur_edges[i] = link[i];   
  80.     int argu_flow = INFINITY;   
  81.     int u = src;   
  82.     while(dist[src] < n)   
  83.     {   
  84.         if(u == des)   
  85.         {   
  86.             for(i = src; i != des; i = cur_edges[i]->ver)   
  87.                 argu_flow = min(argu_flow, cur_edges[i]->cap);   
  88.             for(i = src; i != des ; i = cur_edges[i]->ver)   
  89.             {   
  90.                 cur_edges[i]->cap -= argu_flow;   
  91.                 cur_edges[i]->rev->cap += argu_flow;   
  92.                 cur_edges[i]->flow += argu_flow;   
  93.                 cur_edges[i]->rev->flow -= argu_flow;   
  94.             }   
  95.             total_flow += argu_flow;   
  96.             u = src;   
  97.         }   
  98.         edge *e;   
  99.         for(e = cur_edges[u]; e; e = e->next)   
  100.             if(e->cap > 0 && dist[u] == dist[e->ver] + 1)   
  101.                 break;   
  102.         if(e)   
  103.         {   
  104.             cur_edges[u] = e;   
  105.             rev_path[e->ver] = e->rev;   
  106.             u = e->ver;   
  107.         }   
  108.         else   
  109.         {   
  110.             if(--h[dist[u]] == 0)   
  111.                 break;   
  112.             cur_edges[u] = link[u];   
  113.             int mini_dist = n;   
  114.             for(edge *e =  link[u]; e; e = e->next)   
  115.                 if(e->cap > 0)   
  116.                     mini_dist = min(mini_dist, dist[e->ver]);   
  117.             dist[u] = mini_dist + 1;   
  118.             h[dist[u]]++;   
  119.             if(u != src)   
  120.                 u = rev_path[u]->ver;   
  121.         }   
  122.     }   
  123.     return total_flow;   
  124. }   
  125.   
  126. int main()   
  127. {   
  128.     int T,m,n,src,des;   
  129.     int u,v,d;   
  130.     scanf("%d",&T);   
  131.     while(T--)   
  132.     {   
  133.         scanf("%d%d",&n,&m);   
  134.         num=0;  
  135.         memset(link, 0, sizeof(link));   
  136.         memset(out,0,sizeof(out));  
  137.         memset(in,0,sizeof(in));  
  138.         src=n+1;  
  139.         des=n+2;  
  140.         while(m--)   
  141.         {   
  142.             scanf("%d%d%d",&u,&v,&d);   
  143.             if(u == v)   
  144.                 continue;   
  145.             out[u]++;   
  146.             in[v]++;   
  147.             if(!d)   
  148.             {   
  149.                 addedge(u,v,1);   
  150.                
  151.             }   
  152.         }   
  153.         int flag=0;   
  154.         int sum=0;   
  155.         for(int i=1;i<=n;i++)   
  156.         {   
  157.             if(abs(in[i]-out[i])%2 == 1)   
  158.             {   
  159.                 flag=1;   
  160.                 break;   
  161.             }   
  162.             if(in[i]<out[i])   
  163.             {   
  164.                 addedge(src,i,(out[i]-in[i])/2);   
  165.                
  166.                 sum += (out[i]-in[i])/2;   
  167.             }   
  168.             else    
  169.             {   
  170.                 addedge(i,des,(in[i]-out[i])/2);   
  171.                
  172.             }   
  173.         }   
  174.         if(flag)     
  175.         {     
  176.             printf("impossible\n");     
  177.             continue;   
  178.         }     
  179.         n=n+2;  
  180.         int ans=sap(n,src,des);//n是点的个数(n+2个)  点下标从1开始到 n+2(des)  
  181.         if(sum == ans)   
  182.             printf("possible\n");   
  183.         else    
  184.             printf("impossible\n");   
  185.     }   
  186.     return 0;   
  187. }   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值