POJ 1637 混合图欧拉回路判断 最大流模板

题意 给你一个混合图(有向边+无向边),判断是否存在欧拉回路

思路 设d(i) = 出度(i) - 入度(i)。无向边随意定向,先并查集判连通,判di是否都为偶数,不是则不行。

建图,加入s,t两点,对di>0的,s->i建边,最大流量为di/2,对di<0,i->t建边,最大流量为-di/2。把随意定向的无向边,加入图中,最大流量为1.跑最大流,如果最后从s出的边都满流,则有欧拉回路。把那些流量为1的边反向即可加入原图中即可完成无向边的定向。

注意:满流的判断,就用一条边一条边的去看~别瞎用什么别的判别法~

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn = 202;
const int maxm = 1003;
const int inf = 0x3f3f3f3f;
int n,m;
int g[maxn][maxn][2];
int d[maxn];
struct EdmondsKarp{
	//用到外部变量maxn,maxm,具体使用不非得按照模板 
	int n,m;
	int g[maxn][maxn];
	int flow[maxn];
	int path[maxn];
	queue<int> q; 
	void init(int nn=0)
	{
		n = nn;
		memset(g,0,sizeof(g));
		memset(flow,0,sizeof(flow));
		memset(path,-1,sizeof(path));
	}
	void addOne(int u,int v,int yuan)
	{
		g[u][v] += yuan;
		m += 2; 
	}
	
	int bfs(int s,int t)
	{
		int i;
		memset(path,-1,sizeof(path));
		flow[s] = inf;
		//如果不加这句话,bfs内部就要一共三个判断 
		path[s] = s; 
		q.push(s);
		while(q.size()>0)
		{
			int tmp = q.front();
			q.pop();
			//这里i从0开始还是1开始,视情况而定 
			for(i=0;i<n;i++)
			{
				if(g[tmp][i]>0 && path[i]==-1)
				{
					flow[i] = min(g[tmp][i],flow[tmp]);
					path[i] = tmp;
					q.push(i);
					if(i==t)
					{
						break;
					}
				}	
			}
			if(path[t] != -1)
				break;
		}
		while(q.size()>0)
			q.pop();
		if(path[t] == -1)
			return 0;
		else	
			return flow[t];
	}
	
	int go(int s,int t)
	{
		int ret = 0;
		while(bfs(s,t))
		{
			int now = path[t];
			int pre = t;
			ret += flow[t];
			while(pre != s)
			{
				g[now][pre] -= flow[t];
				g[pre][now] += flow[t];
				pre = now;
				now = path[now];
			}
		}
		return ret;
	}
	
}edk;

int fa[maxn];
int find(int x)
{
	if(fa[x] == x)
		return x;
	else
		return fa[x] = find(fa[x]);
}
void unin(int x,int y)
{
	x = find(x);
	y = find(y);
	if(x==y)
		return;
	fa[x] = y;
}


void addEdge()
{
	scanf("%d%d",&n,&m);
	memset(g,0,sizeof(g));
	memset(d,0,sizeof(d));
	for(int i=1;i<=n;i++)
		fa[i] = i;
	for(int i=0;i<m;i++)
	{
		int u,v,b;
		scanf("%d%d%d",&u,&v,&b);
		if(b==1)
			g[u][v][1]++;
		else
			g[u][v][0]++;
		//出度减入度 
		d[u]++;
		d[v]--;
		unin(u,v);
	}
}

void create()
{
	edk.init(n+2);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(g[i][j][0])
				edk.addOne(i,j,g[i][j][0]);
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(d[i] > 0)
		{
			edk.addOne(0,i,d[i]/2);
		}
		else if(d[i] < 0)
		{
			edk.addOne(i,n+1,-d[i]/2);
		}
	}
}



bool judge()
{
	for(int i=2;i<=n;i++)
	{
		if(find(i) != find(i-1))
			return false;
	}
	for(int i=1;i<=n;i++)
	{
		if(d[i] % 2 != 0)
			return false;
	}
	return 1;
}

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		addEdge();
		if(judge() == 0)
		{
			puts("impossible");
			continue;
		}
		create();
		edk.go(0,n+1);
		int sum = 0;
		int i = 1;
		for(i=1;i<=n;i++)
		{
			if(d[i] > 0)
			{
				if(edk.g[0][i] > 0)
					break;
			}
		}
		if(i == n+1)
			puts("possible");
		else
			puts("impossible");
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值