Poj 1637 Sightseeing tour (混合圖的歐拉回路判定)
阿新 • • 發佈:2019-02-15
題意:給出一個混合圖,要求判定歐拉回路是否存在,輸入 x y d,如果d為0則為無向邊,為1則為有向邊。
首先應該判定圖的連通性!本題所給圖均是連通的,所以沒有判斷。
對所有的無向邊隨便定向,之後再進行調整。
統計每個點的出入度,如果有某個點出入度之差為奇數,則不存在歐拉回路,因為相差為奇數的話,無論如果調整邊,都不能使得每個點的出入度相等。
現在每個點的出入度之差為偶數了,把這個偶數除以2,得x。則對每個頂點改變與之相連的x條邊的方向就可以使得該點出入度相等。如果每個點都能達到出入度相等,自然就存在歐拉回路了。
現在問題就變成了改變哪些邊的方向能讓每個點出入度相等了,構造網路流模型。
有向邊不能改變方向,所以不新增有向邊。對於在開始的時候任意定向的無向邊,按所定的方向加邊,容量為1。
對於剛才提到的x,如果x大於0,則建一條s(源點)到當前點容量為x的邊,如果x小於0,建一條從當前點到 t(匯點)容量為|x|的邊。
這時與原點相連的都是缺少入度的點,與匯點相連的都是缺少出度的點,
建圖完成了,求解最大流,如果能滿流分配,則存在歐拉回路。那麼哪些邊改變方向才能得到歐拉回路呢?檢視流量分配,所有流量非0的邊就是要改變方向的邊。
原理是因為滿流分配,所以和源點相連的點一定都有x條邊流入,將這些邊反向這些點就出入度相等了,和匯點相連的亦然。沒有和源、匯相連的已經出入度相等了,當然不用修改,至此歐拉回路求解完畢。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int INF = 0x3fffffff ; //權值上限 const int MAXPT = 205 ; //頂點數上限 const int MAXEG = 5005 ; //邊數上限 const int MAXQUE = 10005 ; // 佇列長度 /* s = 1 ; // 源點 t 根據初始化函式的不同改變 */ template<typename Type> class MNF_SAP { private: int s,t; int dis[MAXPT]; //距離標號 int pre[MAXPT]; //前置頂點 Type flow[MAXPT]; //到當前點為止,所有弧的最小值 int curedge[MAXPT]; //當前弧cur int cnt[MAXPT]; //k標號出現次數 int queue[MAXQUE],front,rear; bool vis[MAXPT]; void BFS () { int i,u; memset(vis,false,sizeof(vis)); front=rear=0; dis[t]=0; vis[t]=true; queue[++rear]=t; while (front!=rear) { u=queue[(++front)%MAXQUE]; for (i=head[u];i!=0;i=edges[i].next) if (vis[edges[i].v]==false && !edges[i].cap) { dis[edges[i].v]=dis[u]+1; vis[edges[i].v]=true; queue[(++rear)%MAXQUE]=edges[i].v; } } for (i=1;i<=n;i++) cnt[dis[i]]++; } public: struct Node { int v,next; Type cap; Node(){} Node (int _v,Type _cap,int _next) { v=_v; cap=_cap; next=_next; } }edges[MAXEG]; int n; //總節點數 int e; int head[MAXPT]; void Init (int _n) //演算法初始化 { s=1,t=_n; //源點1匯點n n=_n; e=2; memset (head,0,sizeof(head)); } void Init (int _s,int _t) //演算法初始化,後續需要對n賦值 { s=1; //源點1,匯點指定 t=_t; e=2; memset (head,0,sizeof(head)); } void Add (int u,int v,Type cap) //始,終,量 { edges[e]=Node(v,cap,head[u]); head[u]=e++; edges[e]=Node(u,0,head[v]); head[v]=e++; } Type SAP () { int u,v,i; Type maxflow=0; //總最大流 u=s; flow[s]=INF; for (i=1;i<=n;i++) curedge[i]=head[i]; //當前弧初始化 BFS (); cnt[0]=n; while (dis[s]<n) { for (i=curedge[u];i!=0;i=edges[i].next) //找允許弧 if (edges[i].cap>0 && dis[edges[i].v]+1==dis[u]) // break; if (i!=0) //存在允許弧 { curedge[u]=i; //設定當前弧 v=edges[i].v; if (edges[i].cap<flow[u]) flow[v]=edges[i].cap; else flow[v]=flow[u]; //標記當前頂點為止經過的最小弧 u=v; pre[v]=i; //前置頂點邊號 if (u==t) { do { edges[pre[u]].cap-=flow[t]; //正向弧減a[t] edges[pre[u]^1].cap+=flow[t]; //通過異或操作找反向弧 u=edges[pre[u]^1].v; } while (u!=s); maxflow+=flow[t]; //memset(flow,0,sizeof(flow)); flow[s]=INF; } } else //不存在允許弧 { if (--cnt[dis[u]]==0) break; //間隙優化 dis[u]=n; curedge[u]=head[u]; for (i=head[u];i!=0;i=edges[i].next) if (edges[i].cap && dis[edges[i].v]+1<dis[u]) dis[u]=dis[edges[i].v]+1; //修改距離標號為 最小的非允許弧加1 cnt[dis[u]]++; if (u!=s) u=edges[pre[u]^1].v; } } return maxflow; } }; MNF_SAP<int> ob; int n,degree[210]; //不足的入度 bool Judge (int n) { int sum=0; for (int i=1;i<=n;i++) if (degree[i]%2==1) //為奇數不可能存在 return false; else { if (degree[i]>0) { ob.Add (1,1+i,degree[i]/2); sum+=degree[i]/2; } else ob.Add(1+i,n+2,-degree[i]/2); //連向匯點 } if (ob.SAP()==sum) return true; else return false; } int main () { int T,m; scanf("%d",&T); while (T--) { scanf("%d%d",&n,&m); ob.Init(n+2); memset(degree,0,sizeof(degree)); for (int i=1;i<=m;i++) { int x,y,d; scanf("%d%d%d",&x,&y,&d); degree[x]++; degree[y]--; if (d==0) //無向圖 ob.Add (1+x,1+y,1); } if (Judge(n)) printf("possible\n"); else printf("impossible\n"); } return 0; }