poj1637 Sightseeing tour (混合图欧拉回路)

有向图存在欧拉回路的条件:所有点入度=出度。

对于混合图,可以先把所有的无向边假设一个方向。

但是所有的假设不一定都正确,此时需要调整边的方向。

怎样调整呢?这是就要使用网络流。

设每个点入度为in[i],出度为out[i],

对于每一个点i,如果in[i] > out[i],从源点到i连一条容量为(in[i] - out[i]) / 2的边,

如果out[i] > in[i],从i到汇点连一条容量为(out[i] - in[i]) / 2的边。

若两点i,j之间有cnt[i][j]条无向边,从j到i连一条容量为cnt[i][j]的边。

求一次最大流,如果满流则有解,否则无解。


代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>

#define INF 0x3f3f3f3f
#define T (n + 1)

using namespace std;

struct Edge{
	int from, to, cap, flow;
	Edge() {}
	Edge(int a, int b, int c, int d) : from(a), to(b), cap(c), flow(d) {}
};

int cas;
int n, m;
int in[205], out[205];
int cnt[205][205]; // cnt[i][j]表示节点i与节点j之间有多少无向边。
vector<Edge> edges;
vector<int> G[205];
void addEdge(int from, int to, int cap) {
	edges.push_back(Edge(from, to, cap, 0));
	edges.push_back(Edge(to, from, 0, 0));
	int siz = edges.size();
	G[from].push_back(siz - 2);
	G[to].push_back(siz - 1);
}

void buildGraph() {
	for (int i = 1; i <= n; i++) {
		if (in[i] > out[i]) addEdge(0, i, (in[i] - out[i]) >> 1);
		if (out[i] > in[i]) addEdge(i, T, (out[i] - in[i]) >> 1);
		for (int j = 1; j <= n; j++)
			if (cnt[i][j]) addEdge(j, i, cnt[i][j]);
	}
}

int cur[205];
int layer[205];
bool build() {
	memset(layer, -1, sizeof(layer));
	queue<int> q;
	q.push(0);
	layer[0] = 0;
	while (!q.empty()) {
		int current = q.front();
		q.pop();
		for (int i = 0; i < G[current].size(); i++) {
			Edge e = edges[G[current][i]];
			if (layer[e.to] == -1 && e.cap > e.flow) {
				layer[e.to] = layer[current] + 1;
				q.push(e.to);
			}
		}
	}
	return layer[T] != -1;
}

int find(int x, int curflow) {
	if (x == T || !curflow) return curflow;
	int f, flow = 0;
	for (int &i = cur[x]; i < G[x].size(); i++) {
		Edge &e = edges[G[x][i]];
		if (layer[e.to] == layer[x] + 1
			&& (f = find(e.to, min(curflow, e.cap - e.flow)))) {
			e.flow += f;
			edges[G[x][i] ^ 1].flow -= f;
			flow += f;
			curflow -= f;
			if (!curflow) break;
		}
	}
	return flow;
}

int dinic() {
	int maxflow = 0;
	while (build()) {
		memset(cur, 0, sizeof(cur));
		maxflow += find(0, INF);
	}
	return maxflow;
}

int main() {
	freopen("1637.in", "r", stdin);
	scanf("%d", &cas);
	while (cas--) {
		scanf("%d %d", &n, &m);
		memset(in, 0, sizeof(in));
		memset(out, 0, sizeof(out));
		memset(cnt, 0, sizeof(cnt));
		for (int i = 0; i <= n; i++)
			G[i].clear();
		edges.clear();
		int a, b, c;
		for (int i = 0; i < m; i++) {
			scanf("%d %d %d", &a, &b, &c);
			in[b]++;
			out[a]++;
			if (!c) cnt[a][b]++;
		}
		int flow = 0; // 总共需要调整的边
		bool flag = true;
		for (int i = 1; i <= n; i++) {
			if (in[i] > out[i])
				flow += (in[i] - out[i] >> 1); // 如果一个点入度大于出度x,那么就需要调整x/2条边
			if (abs(in[i] - out[i]) & 1) { // 如果一个点入度-出度的绝对值为奇数
				flag = false; // 一定不存在欧拉回路
				break;
			}
		}
		if (!flag) {
			printf("impossible\n");
			continue;
		}
		buildGraph();
		int maxflow = dinic();
		if (maxflow == flow) printf("possible\n");
		else printf("impossible\n");
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值