于2023/2/22日的模拟赛遇到了这一东西。也是网络流应用的一种新模型,感觉是大有可为啊,写个博客记录下。

给定一个图,里面的边有的是有向边,有的是无向边,要求给出无向边的定向方案,使得图中有欧拉回路。如果无解,则输出 \(-1\)

核心思想就是先随便给无向边定个向,再利用网络流的调整功能来调节边的方向,使得图中有欧拉回路。

步骤:

  • 给所有无向边随便定一个方向,然后计算每个点的出度和入度,建立一个超级源点和超级汇点。
  • 如果一个点的入度大于出度,那么就给它向超级汇点连一条最大流量为 \(\frac{入度-出度}{2}\) 的边。如果一个点的出度大于入度,就从超级源点向它连一条 \(\frac{出度 - 入度}{2}\) 的边。
  • 直接跑网络流,如果满流,则有解,否则无解,输出 \(-1\)
  • 如果有解的话,就遍历一下无向边,如果在网络流中,这条边满流了,那么就把它反向。最后输出方案即可。

原理:

个人认为最难理解的就是第二点,为什么要这样连边呢?欧拉回路要求的就是使得图中每个点的入度等于出度。如果一个点的入度大于出度,把它与源点相连,流从连接他的边流出,就相当于改变了边的方向,调整了出入度。另一种情况也同理。

无解的时候,为什么图不能满流?无解,就相当于网络流无法将图中的每个点调整为出度等于入度,因此就无解了。

放个板子:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6;
const int INF = 0x3f3f3f3f;
int id[MAXN + 5],n,m,tot,deg[MAXN + 5],node[MAXN + 5],cno,head[MAXN + 5],s,t,dis[MAXN + 5],cur[MAXN + 5],ans[MAXN + 5],cnt;
bool vis[MAXN + 5];
vector<int> lsh;
struct E{
	int l,r,w;
}e[MAXN + 5];
struct EDGE{
	int u,v,w,next;
}edge[MAXN + 5];
void ADD(int u,int v,int w){
	++tot;
	edge[tot].u = u;
	edge[tot].v = v;
	edge[tot].w = w;
	edge[tot].next = head[u];
	head[u] = tot;
}
void add(int u,int v,int w){
	ADD(u,v,w);
	ADD(v,u,0);
}
bool bfs(){
	queue<int> q;
	q.push(s);
	memset(dis,0,sizeof dis);
	dis[s] = 1;
	while(!q.empty()){
		int u = q.front();
		q.pop();
		for(int i = head[u]; i; i = edge[i].next){
			int v = edge[i].v;
			if(!dis[v] && edge[i].w > 0){
				dis[v] = dis[u] + 1;
				q.push(v);
			}
		}
	}
	if(dis[t])return 1;
	return 0;
}
int dfs(int u,int dist){
	if(u == t)return dist;
	for(int &i = cur[u]; i; i = edge[i].next){
		int v = edge[i].v;
		if(dis[v] == dis[u] + 1 && edge[i].w > 0){
			int di = dfs(v,min(edge[i].w,dist));
			if(di > 0){
				edge[i].w -= di;
				edge[i ^ 1].w += di; 
				return di;
			}
		}
	}
	return 0;
}
int dinic(){
	int an = 0;
	while(bfs()){
		for(int i = s; i <= t; i++){cur[i] = head[i];}
		while(int di = dfs(s,INF))an += di;
	}
	return an;
}
int main(){
	freopen("wait.in","r",stdin);
	freopen("wait.out","w",stdout);
	tot = 1;
	scanf("%d%d",&m,&n);
	for(int i = 1; i <= m; i++){
		scanf("%d%d%d",&e[i].l,&e[i].r,&e[i].w);
		e[i].r++;
		lsh.push_back(e[i].l);
		lsh.push_back(e[i].r);
	}
	sort(lsh.begin(),lsh.end());
	lsh.erase(unique(lsh.begin(),lsh.end()),lsh.end());
	for(int i = 1; i <= m; i++){
		e[i].l = lower_bound(lsh.begin(),lsh.end(),e[i].l) - lsh.begin() + 1;
		e[i].r = lower_bound(lsh.begin(),lsh.end(),e[i].r) - lsh.begin() + 1;
	}
	for(int i = 1; i <= m; i++){
		if(e[i].w == -1){
			add(e[i].l,e[i].r,1);
			ans[tot / 2] = 1;
			deg[e[i].l]++;
			deg[e[i].r]--;
			id[i] = tot;
		}
		else if(e[i].w == 1)deg[e[i].l]++,deg[e[i].r]--;
		else deg[e[i].r]++,deg[e[i].l]--; 
	}
	s = 0,t = lsh.size() + 1;
	for(int i = 1; i <= lsh.size(); i++){
		if(deg[i] >= 0){
			add(s,i,deg[i] / 2);
			cnt += deg[i] / 2;
		}
		else{
			add(i,t,(-deg[i] / 2));
		}
	}
	int k;
	k = dinic();
	if(k < cnt){
		cout << "-1";
		return 0;
	}
	for(int i = 1; i <= m; i++){
		if(e[i].w == -1) cout << (edge[id[i]].w ^ 1) << " ";
		else cout << e[i].w << " ";
	}
}
//4 1000000000
//1 9 -1
//2 6 -1
//1 8 -1
//1 7 -1

利用网络流的动态调整功能,可以解决这样的很多问题。