poj1637

求混合图的欧拉回路;

在网上看了一些算法(http://baike.baidu.com/view/566040.htm),说说自己的理解,

有向图存在欧拉回路条件:每个点处度等于入度

现在可以假定每个无向边一个方向,这样假定后出度和入度差会改变2

如果所有的假设都对,那么最后每个点的出度都等于入度

但有的边可能假设错,这就需要用网络流来调整,假设最后出度大于入度为n,那么需要改变n/2条出边的方向,

选择哪儿几条那,可以通过引入一个源点到这个点容量为n/2的边,如果流的通,那么就改变这几条边的方向,同时说明其他点也是需要调整的,

所以算法就出来了

1.对每条无向边假设方向,建一条容量为1的边

2.计算每个点的出度-入度=n,大于零引入s送到点容量n/2的边,<0引入到t为n/2的边

3.计算流看是不是满流,是满流就表示可以构成欧拉回路

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<limits.h>
#include<queue>
#define MAX 220
using namespace std;
struct node
{
    int y;
    int c;
    int f;
    int next;
}a[3000];
int head[MAX];
int tot,vi[MAX],vo[MAX];
int leve[MAX];
void addedge(int x,int y ,int c)
{
    a[tot].y=y;
    a[tot].c=c;
    a[tot].f=0;
    a[tot].next=head[x];
    head[x]=tot++;
    a[tot].y=x;
    a[tot].c=0;
    a[tot].f=0;
    a[tot].next=head[y];
    head[y]=tot++;
}
bool bfs(int s,int t)
{
    int x,y,i,u,v;
    memset(leve,-1,sizeof(leve));
    queue<int> q;
    q.push(s);
    leve[s]=0;
    while(!q.empty())
    {
        u=q.front();
        q.pop();
        i=head[u];
        while(i!=-1)
        {
            v=a[i].y;
            if(a[i].c>0&&leve[v]==-1)
            {
                leve[v]=leve[u]+1;
                q.push(v);
            }
            i=a[i].next;
        }
    }
    return leve[t]!=-1;
}
int dinic(int s,int t)
{
    int stack[MAX],last[MAX];
    int u,v,top=1,minf,i,temp,c,edge,sum=0;
    while(bfs(s,t))
    {
        top=1;
        stack[top]=s;
        for(i=s;i<=t;i++)
        {
            last[i]=head[i];
        }
        while(top)
        {
            u=stack[top];
            if(u==t)
            {
                minf=INT_MAX;
                for(i=1;i<top;i++)
                {
                    edge=last[stack[i]];
                    if(minf>a[edge].c)
                    {
                        minf=a[edge].c;
                        temp=i;
                    }
                }
                for(i=1;i<top;i++)
                {
                    edge=last[stack[i]];
                    a[edge].c-=minf;
                    a[edge].f+=minf;
                    a[edge^1].c+=minf;
                    a[edge^1].f-=minf;
                }
                sum+=minf;
                top=temp;
                continue;
            }
            edge=last[u];
            while(edge!=-1)
            {
                v=a[edge].y;
                c=a[edge].c;
                if(c>0&&leve[u]+1==leve[v])
                {
                    stack[++top]=v;
                    break;
                }
                edge=a[edge].next;
            }
            last[u]=edge;
            if(edge==-1)
            {
                top--;
                if(top!=0)
                  last[stack[top]]=a[last[stack[top]]].next;
            }
        }
    }
    return sum;
}

int main()
{
    int t,k,i,j,sum=0,m,n;
    int fu,fv;
    int to,from;
    bool flag;
    scanf("%d",&t);
    while(t--)
    {
        memset(vo,0,sizeof(vo));
        memset(vi,0,sizeof(vi));
        memset(head,-1,sizeof(head));
        tot=0;
        sum=0;
        flag=true;
        scanf("%d%d",&m,&n);
        while(n--)
        {
            scanf("%d%d%d",&from,&to,&i);
            vo[from]++;
            vi[to]++;
            if(!i)
            {
                addedge(from,to,1);
            }
        }
        for(i=1;i<=m;i++)
        {
            k=vo[i]-vi[i];
            if(k%2==0)
            {
                if(k/2>0)
                {
                    addedge(0,i,k/2);
                    sum+=k/2;
                }
                else
                  {
                      addedge(i,m+1,-k/2);
                  }
            }
            else
            {
                flag=false;
                break;
            }
        }
        if(flag)
        {
            if(sum!=dinic(0,m+1))
              flag=false;
        }
        if(flag)
          printf("possible\n");
        else
          printf("impossible\n");
    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值