程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> poj 1637 混合歐拉回路 網絡流 dinic 以及sap 算法

poj 1637 混合歐拉回路 網絡流 dinic 以及sap 算法

編輯:C++入門知識

題意:給出一個混合圖(有的邊有向,有的邊無向),問此圖是否存在歐拉回路。

先說說歐拉回路吧,起點和終點相同,經過圖G的每條邊一次,且只經過一次的路徑稱為歐拉回路。
按照圖的不同分為:無向圖歐拉回路、有向圖歐拉回路和混合圖歐拉回路。

判斷一個圖是否存在歐拉回路:
1.無向圖:圖連通,且圖中均為偶度頂點。
2.有向圖:圖連通,且圖中所有頂點出入度相等。
3.混合圖:混合圖歐拉回路的判斷是用網絡流,實現方法:

首先對所有的無向邊隨便定向,之後會進行調整。然後統計每個點的出入度,如果有某個點出入度之差為奇數,則不存在歐拉回路,因為相差為奇數的話,無論如果調整邊,都不能使得每個點的出入度相等。

現在每個點的出入度之差為偶數了,把這個偶數除以2,得x。則對每個頂點改變與之相連的x條邊的方向就可以使得該點出入度相等。如果每個點都能達到出入度相等,自然就存在歐拉回路了。

現在問題就變成了改變哪些邊的方向能讓每個點出入度相等了,構造網絡流模型。
有向邊不能改變方向,所以不添加有向邊。對於在開始的時候任意定向的無向邊,按所定的方向加邊,容量為1。源點向所有出>入的點連邊,容量為該點的x值;所有入>出的點向匯點連邊,容量為該點的x值。

建圖完成了,求解最大流,如果能滿流分配,則存在歐拉回路。那麼哪些邊改變方向才能得到歐拉回路呢?查看流量分配,所有流量非0的邊就是要改變方向的邊。

原理是因為滿流分配,所以和源點相連的點一定都有x條邊流入,將這些邊反向這些點就出入度相等了,和匯點相連的亦然。沒有和源、匯相連的已經出入度相等了,當然不用修改,至此歐拉回路求解完畢。

 

  

我個人的理解:  首先 上面紅色的   是由於  在歐拉回路中每個點的出度都等於入度。 那麼當一個點的出度增加1 那麼入度一定減少1 那麼他們的差依舊保持為偶數 如果不是 說明這個圖不會是歐拉回路

 

我們把無向邊隨意變成有向邊後    會存在有些點的入度不等於出度  ,那麼我們需要改變以前的那些無向邊中的部分邊  使得每個點都能夠出度等於入度。

 如何修改那 ?   按照上面的方式連接各個邊之後    形成的圖的意思 就是: 從s到所有邊中找出部分邊修改使得與s相連的每個點的入度等於出度 同時使得與t相連的每個點的出度等於入度  

DINIC算法

#include <stdio.h>
#include <string.h>
#define VM 222
#define EM 20550
#define inf 0x3f3f3f3f
struct Edge
{
    int frm,to,cap,next;
}edge[EM];

int head[VM],dep[VM],ep;     //dep為點的層次
void addedge (int cu,int cv,int cw)  //第一條邊下標必須為偶數
{
    edge[ep].frm = cu;
    edge[ep].to = cv;
    edge[ep].cap = cw;
    edge[ep].next = head[cu];
    head[cu] = ep;
    ep ++;
    edge[ep].frm = cv;
    edge[ep].to = cu;
    edge[ep].cap = 0;
    edge[ep].next = head[cv];
    head[cv] = ep;
    ep ++;
}

int BFS (int src,int des)     //求出層次圖
{
    int que[VM],i,front = 0,rear = 0;
    memset (dep,-1,sizeof(dep));
    que[rear++] = src;
    dep[src] = 0;
    while (front != rear)
    {
        int u = que[front++];
        front = front%VM;
        for (i = head[u];i != -1;i = edge[i].next)
        {
            int v = edge[i].to;
            if (edge[i].cap > 0&&dep[v] == -1) //容量大於0&&未在dep中
            {
                dep[v] = dep[u] + 1;        //建立層次圖
                que[rear ++] = v;
                rear = rear % VM;
                if (v == des)  //找到匯點 返回
                    return 1;
            }
        }
    }
    return 0;
}
int dinic (int src,int des)
{
    int i,res = 0,top;
    int stack[VM];    //stack為棧,存儲當前增廣路
    int cur[VM];        //存儲當前點的後繼 跟head是一樣的
    while (BFS(src,des))   //if BFS找到增廣路
    {
        memcpy (cur,head,sizeof (head));
        int u = src;       //u為當前結點
        top = 0;
        while (1)
        {
            if (u == des)     //增廣路已全部進棧
            {
                int min = inf,loc ;
                for (i = 0;i < top;i ++)       //找最小的增廣跟並loc記錄其在stack中位置
                    if (min > edge[stack[i]].cap)  //以便退回該邊繼續DFS
                    {
                        min = edge[stack[i]].cap;
                        loc = i;
                    }
                for (i = 0;i < top;i ++)   //偶數^1 相當加1 奇數^1相當減1 當正向邊 = 0&&路徑不合適時,正加負減
                {                           //偶數是正向邊,奇數是負向邊,邊從0開始
                    edge[stack[i]].cap -= min;
                    edge[stack[i]^1].cap += min;
                }                              //將增廣路中的所有邊修改
                res += min;
                top = loc;
                u = edge[stack[top]].frm;         //當前結點修改為最小邊的起點
            }
            for (i = cur[u];i != -1;cur[u] = i = edge[i].next)   //找到當前結點對應的下一條邊
                if (edge[i].cap != 0&&dep[u] + 1 == dep[edge[i].to])//不滿足條件時,修改cur值(去掉不合適的占)eg:1-->2 1-->3 1-->4 有邊 但只有
                    break;                                  // 1-->4 這條邊滿足條件 就把1到2、3的邊給去掉
            if (cur[u] != -1)            //當前結點的下一條邊存在
            {
                stack[top ++] = cur[u];   //把該邊放入棧中
                u = edge[cur[u]].to;         //再從下個點開始找
            }
            else
            {
                if (top == 0)        //當前結點無未遍歷的下一條邊且棧空,DFS找不到下一條增廣路
                    break;
                dep[u] = -1;            //當前結點不在增廣路中,剔除該點
                u = edge[stack[--top]].frm; //退棧 回朔,繼續查找
            }
        }
    }
    return res;
}

int in[VM],out[VM]; 

int abs(int qa)
{
	if(qa>0) return qa;
	return -qa;
}
int main() 
{ 
    int T,n,m; 
    int u,v,d; 
    scanf("%d",&T); 
    while(T--) 
    { 
        scanf("%d%d",&n,&m); 
		ep=0;
        memset (head,-1,sizeof(head));
		memset(out,0,sizeof(out));
		memset(in,0,sizeof(in));
        while(m--) 
        { 
            scanf("%d%d%d",&u,&v,&d); 
            if(u == v) 
                continue; 
            out[u]++; 
            in[v]++; 
            if(!d) 
            { 
                addedge(u,v,1); 
            } 
        } 
        int flag=0; 
        int sum=0; 
        for(int i=1;i<=n;i++) 
        { 
            if(abs(in[i]-out[i])%2 == 1) 
            { 
                flag=1; 
                break; 
            } 
            if(in[i]<out[i]) 
            { 
                addedge(0,i,(out[i]-in[i])/2); 
                sum += (out[i]-in[i])/2; 
            } 
            else  
            { 
                addedge(i,n+1,(in[i]-out[i])/2); 
            } 
        } 
        if(flag)   
        {   
            printf("impossible\n");   
            continue;   
        }   
        int ans = dinic(0,n+1); 
        if(sum == ans) 
            printf("possible\n"); 
        else  
            printf("impossible\n"); 
    } 
    return 0; 
} 
#include <stdio.h>
#include<string.h>
#include<algorithm>
using namespace std; 
#define MAX 210 
#define INFINITY 1000000000 
struct edge 
{ 
    int cap; 
    int flow; 
    int ver; 
    edge *next; 
    edge *rev; 
}; 
edge edges[2500]; 
edge *link[MAX+1]; 
int dist[MAX +1]; 
int h[MAX + 1]; 
int in[MAX+1]; 
int out[MAX+1]; 
 
int num; 
int total_flow; 
int min(int a, int b) 
{ 
    return a < b ? a : b; 
}; 
void addedge(int start, int end, int c) 
{ 
    num++; 
    edges[num].ver = end; 
    edges[num].cap = c; 
    edges[num].next = link[start]; 
    link[start] = edges + num; 
    num++; 
    edges[num].ver = start; 
    edges[num].cap = 0; 
    edges[num].next = link[end]; 
    link[end] = edges + num; 
    link[start]->rev = link[end]; 
    link[end]->rev = link[start]; 
} 
void rev_bfs(int n, int src, int des) 
{ 
    int q[MAX + 1]; 
    int head = 0; 
    int tail = 0; 
    for(int i = 1; i <= n; i++) 
    { 
        dist[i] = MAX; 
        h[i] = 0; 
    } 
    q[tail++] = des; 
    dist[des] = 0; 
    h[0] = 1; 
    int p; 
    while(tail != head) 
    { 
        p = q[head++]; 
        for(edge *e = link[p]; e; e = e->next) 
        { 
            if(dist[e->ver] != MAX || e->rev->cap == 0) 
                continue; 
            dist[e->ver] = dist[p] + 1; 
            h[dist[e->ver]]++; 
            q[tail++] = e->ver; 
        } 
    } 
} 
 
int sap(int n, int src, int des) 
{ 
    rev_bfs(n, src, des); 
    edge *cur_edges[MAX+1]; 
    edge *rev_path[MAX+1]; 
    total_flow = 0; 
    int i; 
    for(i = 1; i <= n ; i++) 
        cur_edges[i] = link[i]; 
    int argu_flow = INFINITY; 
    int u = src; 
    while(dist[src] < n) 
    { 
        if(u == des) 
        { 
            for(i = src; i != des; i = cur_edges[i]->ver) 
                argu_flow = min(argu_flow, cur_edges[i]->cap); 
            for(i = src; i != des ; i = cur_edges[i]->ver) 
            { 
                cur_edges[i]->cap -= argu_flow; 
                cur_edges[i]->rev->cap += argu_flow; 
                cur_edges[i]->flow += argu_flow; 
                cur_edges[i]->rev->flow -= argu_flow; 
            } 
            total_flow += argu_flow; 
            u = src; 
        } 
        edge *e; 
        for(e = cur_edges[u]; e; e = e->next) 
            if(e->cap > 0 && dist[u] == dist[e->ver] + 1) 
                break; 
        if(e) 
        { 
            cur_edges[u] = e; 
            rev_path[e->ver] = e->rev; 
            u = e->ver; 
        } 
        else 
        { 
            if(--h[dist[u]] == 0) 
                break; 
            cur_edges[u] = link[u]; 
            int mini_dist = n; 
            for(edge *e =  link[u]; e; e = e->next) 
                if(e->cap > 0) 
                    mini_dist = min(mini_dist, dist[e->ver]); 
            dist[u] = mini_dist + 1; 
            h[dist[u]]++; 
            if(u != src) 
                u = rev_path[u]->ver; 
        } 
    } 
    return total_flow; 
} 

int main() 
{ 
    int T,m,n,src,des; 
    int u,v,d; 
    scanf("%d",&T); 
    while(T--) 
    { 
        scanf("%d%d",&n,&m); 
        num=0;
		memset(link, 0, sizeof(link)); 
		memset(out,0,sizeof(out));
		memset(in,0,sizeof(in));
		src=n+1;
		des=n+2;
        while(m--) 
        { 
            scanf("%d%d%d",&u,&v,&d); 
            if(u == v) 
                continue; 
            out[u]++; 
            in[v]++; 
            if(!d) 
            { 
                addedge(u,v,1); 
			 
            } 
        } 
        int flag=0; 
        int sum=0; 
        for(int i=1;i<=n;i++) 
        { 
            if(abs(in[i]-out[i])%2 == 1) 
            { 
                flag=1; 
                break; 
            } 
            if(in[i]<out[i]) 
            { 
                addedge(src,i,(out[i]-in[i])/2); 
			 
                sum += (out[i]-in[i])/2; 
            } 
            else  
            { 
                addedge(i,des,(in[i]-out[i])/2); 
			 
            } 
        } 
        if(flag)   
        {   
            printf("impossible\n");   
            continue; 
        }   
		n=n+2;
		int ans=sap(n,src,des);//n是點的個數(n+2個)  點下標從1開始到 n+2(des)
        if(sum == ans) 
            printf("possible\n"); 
        else  
            printf("impossible\n"); 
    } 
    return 0; 
} 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved