建图方法:
把该图的无向边随便定向,计算每个点的入度和出度。如果有某个点出入度之差为奇数,那么肯定不存在欧拉回路。因为欧拉回路要求每点入度 = 出度,也就是总度数为偶数,
若在奇数度点必不能有欧拉回路。
好了,现在每个点入度和出度之差均为偶数。那么将这个偶数除以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;
}