POJ 1637 Sightseeing tour (混合图欧拉回路)

来源:互联网 发布:开关贴淘宝 编辑:程序博客网 时间:2024/05/01 06:48

建图方法:

把该图的无向边随便定向,计算每个点的入度和出度。如果有某个点出入度之差为奇数,那么肯定不存在欧拉回路。因为欧拉回路要求每点入度 = 出度,也就是总度数为偶数,

若在奇数度点必不能有欧拉回路。

好了,现在每个点入度和出度之差均为偶数。那么将这个偶数除以2,得x。也就是说,对于每一个点,只要将x条边改变方向(入>出就是变入,出>入就是变出),就能保证出=

入。如果每个点都是出=入,那么很明显,该图就存在欧拉回路。

现在的问题就变成了:我该改变哪些边,可以让每个点出=入?构造网络流模型。首先,有向边是不能改变方向的,要之无用,删。一开始不是把无向边定向了吗?定的是什么

向,就把网络构建成什么样,边长容量上限1。另新建s和t。对于入>出的点u,连接边(u, t)、容量为x,对于出>入的点v,连接边(s, v),容量为x(注意对不同的点x不同)。之后,

察看是否有满流的分配。有就是能有欧拉回路,没有就是没有。欧拉回路是哪个?察看流值分配,将所有流量非 0(上限是1,流值不是0就是1)的边反向,就能得到每点入度=出

度的欧拉图。

由于是满流,所以每个入>出的点,都有x条边进来,将这些进来的边反向,OK,入=出了。对于出>入的点亦然。那么,没和s、t连接的点怎么办?和s连接的条件是出>入,和t相

接的条件是入>出,那么这个既没和s也没和t连接的点,自然早在开始就已经满足入=出了。那么在网络流过程中,这些点属于“中间点”。我们知道中间点流量不允许有累积的,这样,进去多少就出来多少,反向之后,自然仍保持平衡。

欧拉图的判定:

1.基图连通,====》并查集判定

2.无向图:无奇度顶点

    有向图:所有顶点出入度相等 ====》统计判定

#include <iostream>#include<stdio.h>#include<string.h>#include<queue>#include<cmath>using namespace std;const int maxn=200+10;const int maxm=2000+10;const int inf=1e9+7;int m,n,s;struct Edge{    int to,next;    int cap;}edge[maxm*2];int head[maxn],size;int dis[maxn];//层次网络的编号值,离汇点的距离int father[maxn];int outd[maxn],ind[maxn];void clear()//链式前向星{    size=0;    memset(head,-1,sizeof(head));    memset(ind,0,sizeof(ind));    memset(outd,0,sizeof(outd));}void _add(int from,int to,int cap){    edge[size].to=to;    edge[size].cap=cap;    edge[size].next=head[from];    head[from]=size++;}void add(int a,int b,int c){    _add(a,b,c);    _add(b,a,0);}bool bfs(int s,int t)//对层次网络进行编号{    queue<int> Q;    memset(dis,-1,sizeof(dis));//将所有点的层次设为-1    dis[s]=0;//s入队    Q.push(s);    while(!Q.empty())    {        int v=Q.front();Q.pop();//取队首元素        if(v==t)return true;        for(int i=head[v];i!=-1;i=edge[i].next)//找邻接点        {            if(dis[edge[i].to]==-1&&edge[i].cap>0)//如果存在邻接点并且还未被编入层次网络            {                dis[edge[i].to]=dis[v]+1;                Q.push(edge[i].to);            }        }    }    return false;}int dfs(int x,int t,int maxc)//从节点x到汇点t的最大可增广流量,maxc表示当前可容许的最大容量{    if(x==t)return maxc;    int ret,flow;ret=0;    for(int i=head[x];i!=-1;i=edge[i].next)    {        if(edge[i].cap>0&&dis[edge[i].to]==dis[x]+1)        {            flow=dfs(edge[i].to,t,min(maxc-ret,edge[i].cap));            edge[i].cap-=flow;            edge[i^1].cap+=flow;            ret+=flow;            if(ret==maxc)return ret;        }    }    return ret;}int dinic(int s,int t){    int ans=0;    while(bfs(s,t))//每次重建层次网络    ans+=dfs(s,t,inf);//如果找到一条增广路    return ans;}void make_set(){    for(int i=1;i<=m;i++)    father[i]=i;}int find_set(int x){    if(x!=father[x])    father[x]=find_set(father[x]);    return father[x];}void union_set(int x,int y){    int a=find_set(x);    int b=find_set(y);    if(a==b)return;    father[a]=b;}bool is_connect(){    int cnt=-1;    for(int i=1;i<=m;i++)    if(father[i]==i)cnt++;    if(cnt==0)return true;    return false;}int main(){    freopen("in","r",stdin);    int a,b,c;    scanf("%d",&n);    while(n--)    {        clear();        scanf("%d %d",&m,&s);        make_set();        for(int i=0;i<s;i++)        {            scanf("%d %d %d",&a,&b,&c);            union_set(a,b);            outd[a]++;ind[b]++;            if(c==0)//如果是无向的,变为单向的。            add(a,b,1);        }        if(!is_connect())puts("impossible");        else        {            int tot=0;            bool flag=false;            for(int i=1;i<=m;i++)            {                if(outd[i]>ind[i])                {                    int ans=outd[i]-ind[i];                    if(ans&1){flag=true;break;}                    add(0,i,ans/2);                    tot+=ans/2;                }                else                {                    int ans=ind[i]-outd[i];                    if(ans&1){flag=true;break;}                    add(i,m+1,ans/2);                }            }            if(flag)puts("impossible");            else            {                if(dinic(0,m+1)==tot)puts("possible");//检查是否满流                else puts("impossible");            }        }    }    return 0;}





原创粉丝点击