混合图欧拉回路(核心也就是网络流啦)

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

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

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

步骤:

  • 给所有无向边随便定一个方向,然后计算每个点的出度和入度,建立一个超级源点和超级汇点。
  • 如果一个点的入度大于出度,那么就给它向超级汇点连一条最大流量为 2 的边。如果一个点的出度大于入度,就从超级源点向它连一条 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

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


__EOF__

本文作者Never Gonna Give You Up!
本文链接https://www.cnblogs.com/CZ-9/p/17149750.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   腾云今天首飞了吗  阅读(75)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示