1. 程式人生 > >Poj 1637 Sightseeing tour (混合圖的歐拉回路判定)

Poj 1637 Sightseeing tour (混合圖的歐拉回路判定)

題意:給出一個混合圖,要求判定歐拉回路是否存在,輸入 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;
}