题目链接:
题意:
一幅图 ,给出有向边和无向边,问是否有经过所有边仅一次的欧拉回路
解题思路:
混合图欧拉回路的求解需要用到网络流,具体的建模方法如下:
1、先给所有无向边定向,然后统计所有点的入度和出度,
2、如果某点 入度-出度=奇数 那么一定不能构成欧拉回路 //入度+x 出度-x 度数差奇偶性不变
3、如果某点 出度>入度 建一条与源点连接的边 边容量为 (出度-入度)/2;
如果某点 出度<入度 建一条与汇点连接的边 边容量为 (入度-出度)/2;
4、所有无向边按已定方向建边,边容量为1
5、跑最大流,如果图中的最大流 等于所有与汇点相连的边的容量和 那么就能构成欧拉回路
其实这个跑最大流的过程可以理解为: 让图中所有点入度等于出度的过程
而最大流中的一条通路 则表示:这条通路上的所有边, 应取原方向的相反方向.
这样与源点相连的点的出度减1,入度加1;与汇点相连的点出度+1,入度-1.
最后最大流==sum((入度与出度差)/2) 即表示 所有点都达到 入度等于出度这一目的.
代码:
#include <iostream>
#include <cstring>
#include<cstdio>
#include <queue>
const int MAXN =505;
const int MAXM=440020;
const int INF=0x3f3f3f3f;
using namespace std;
struct Edge
{
int to,cap,flow,next;
} edge[MAXM];
int head[MAXN],tot,gap[MAXN],d[MAXN],cur[MAXN],que[MAXN],p[MAXN];
void init()
{
tot=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int c,int f)
{
edge[tot]=(Edge){v,c,f,head[u]};
head[u] = tot++;
edge[tot]=(Edge){u,c,c,head[v]};
head[v] = tot++;
}
int isap(int source,int sink,int N)
{
memset(gap,0,sizeof(gap));
memset(d,0,sizeof(d));
memcpy(cur,head,sizeof(head));
int top = 0,x = source,flow = 0;
while(d[source] < N)
{
if(x == sink)
{
int Min = INF,inser=0;
for(int i = 0; i < top; ++i)
{
if(Min > edge[p[i]].cap - edge[p[i]].flow)
{
Min = edge[p[i]].cap - edge[p[i]].flow;
inser = i;
}
}
for(int i = 0; i < top; ++i)
{
edge[p[i]].flow += Min;
edge[p[i]^1].flow -= Min;
}
if(Min!=INF) flow += Min;
top = inser;
x = edge[p[top]^1].to;
continue;
}
int ok = 0;
for(int i = cur[x]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(edge[i].cap > edge[i].flow && d[v]+1 == d[x])
{
ok = 1;
cur[x] = i;
p[top++] = i;
x = edge[i].to;
break;
}
}
if(!ok)
{
int Min = N;
for(int i = head[x]; i != -1; i = edge[i].next)
{
if(edge[i].cap > edge[i].flow && d[edge[i].to] < Min)
{
Min = d[edge[i].to];
cur[x] = i;
}
}
if(--gap[d[x]] == 0) break;
gap[d[x] = Min+1]++;
if(x != source) x = edge[p[--top]^1].to;
}
}
return flow;
}
int main()
{
// freopen("in.txt","r",stdin);
int in[MAXN],out[MAXN];
int T,n,m,a,b,c;
scanf("%d",&T);
while(T--)
{
init();
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
scanf("%d%d",&n,&m);
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
out[a]++,in[b]++;
if(c==0)
addedge(a,b,1,0);
}
int flag=0,sum=0,d;
for(int i=1; i<=n; i++)
{
d=in[i]-out[i];
if(d&1)
flag=1;
else if(d<0)
addedge(0,i,(-d)>>1,0);
else if(d>0)
{
addedge(i,n+1,d>>1,0);
sum+=d>>1;
}
}
if(sum!=isap(0,n+1,n+2))
flag=1;
if(flag==1) printf("impossible\n");
else printf("possible\n");
}
return 0;
}