您的位置:首页 > 其它

Poj 1637 Sightseeing tour (混合图的欧拉回路判定)

2014-03-07 12:29 459 查看
题意:给出一个混合图,要求判定欧拉回路是否存在,输入 x y d,如果d为0则为无向边,为1则为有向边。

关于有向图和无向图的欧拉回路可以参考我的另一篇博文 欧拉通路 欧拉回路的判定 Hdu 1878 欧拉回路

以下内容参考了:ZOJ 1992 & POJ 1637 (混合图欧拉回路) | 翅膀~

首先应该判定图的连通性!本题所给图均是连通的,所以没有判断。

对所有的无向边随便定向,之后再进行调整。

统计每个点的出入度,如果有某个点出入度之差为奇数,则不存在欧拉回路,因为相差为奇数的话,无论如果调整边,都不能使得每个点的出入度相等。

现在每个点的出入度之差为偶数了,把这个偶数除以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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: