zoukankan      html  css  js  c++  java
  • 紫书 例题 11-13 UVa 10735(混合图的欧拉回路)(最大流)

    这道题写了两个多小时……


    首先讲一下怎么建模

    我们的目的是让所有点的出度等于入度

    那么我们可以把点分为两部分, 一部分出度大于入度, 一部分入度大于出度

    那么显然, 按照书里的思路,将边方向后,就相当于从出度大于入度的运一个流量到

    入度大于出度的点。

    紫书 例题 11-13 UVa 10735(混合图的欧拉回路)(最大流)

    所以我们可以把源点S到所有出度大于入度的点连一条弧, 弧的容量是出度-入度的一半


    为什么容量是这样呢,等一下说

    同理, 把所有入度大于出度的点和汇点T连一条弧, 弧的容量是入度-出度的一半

    同时,所有无向边任意选一个方向, 例如选u到v, 那么容量为1, 表示这条无向边

    反转之后可以运一个出度过去。

    所以, 如果这个图满载的话, 也就是说有欧拉回路

    因为, 比如说出度大于出度的点, 如果满载,说明它肯定运了出度-入度的一半的流量

    那么这个点的自身就符合了出度等于入度。

    以此类推, 如果满载,那么所有点都满足入度等于出度, 就有欧拉回路。

    然后是输出, 要把图建出来(不是网络流的图, 是为了输出而用的图)


    无向图的方向可以扫一遍所有的弧, 只要起点和终点都不是汇点与源点, 同时容量不为0(这是反向弧)

    那么这条弧就是由无向边建立来的。

    如果这条弧满载, 说明边反向了,那么就在从这条弧的终点向起点连一条边

    如果不满载, 说明没有反向, 那么就从这条弧的起点向终点连一条边

    然后就dfs输出路径就好了

    这里要注意题目有重边和自环, vis数组要特殊处理(看代码)
    #include<cstdio>
    #include<cmath>
    #include<vector>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #define REP(i, a, b) for(int i = (a); i < (b); i++)
    using namespace std;
    
    const int MAXN = 112;
    struct Edge { int from, to, cap, flow; };
    vector<Edge> edges;
    vector<int> g[MAXN];
    int n, m, s, t, cur[MAXN];
    int h[MAXN], in[MAXN], out[MAXN], map[MAXN][MAXN], vis[MAXN][MAXN];
    vector<int> path;
    
    void AddEdge(int from, int to, int cap)
    {
    	edges.push_back(Edge{from, to, cap, 0});
    	edges.push_back(Edge{to, from, 0, 0});
    	g[from].push_back(edges.size() - 2);
    	g[to].push_back(edges.size() - 1);
    }
    
    bool bfs()
    {
    	queue<int> q;
    	q.push(s);
    	memset(h, 0, sizeof(h));
    	h[s] = 1;
    	
    	while(!q.empty())
    	{
    		int x = q.front(); q.pop();
    		REP(i, 0, g[x].size())
    		{
    			Edge& e = edges[g[x][i]];
    			if(e.cap > e.flow && !h[e.to])
    			{
    				h[e.to] = h[x] + 1;
    				q.push(e.to);
    			}
    		}
    	}
    	
    	return h[t];
    }
    
    int dfs(int x, int a)
    {
    	if(x == t || a == 0) return a;
    	int flow = 0, f;
    	for(int& i = cur[x]; i < g[x].size(); i++)
    	{
    		Edge& e = edges[g[x][i]];
    		if(h[x] + 1 == h[e.to] && (f = dfs(e.to, min(e.cap - e.flow, a))) > 0)
    		{
    			e.flow += f;
    			edges[g[x][i] ^ 1].flow -= f;
    			flow += f;
    			if((a -= f) == 0) break;
    		}
    	}
    	return flow;
    } 
    
    int maxflow()
    {
    	int flow = 0;
    	while(bfs())
    	{
    		memset(cur, 0, sizeof(cur));
    		flow += dfs(s, 1e9); 
    	} 
    	return flow;
    } //以上是最大流 
    
    bool judge()
    {
    	int ok = 1, sum = 0;
    	REP(i, 0, n)
    		if(in[i] != out[i])
    		{
    			int tmp = abs(in[i] - out[i]);
    			if(tmp & 1) { ok = -1; break; }  
    			else ok = 0, tmp >>= 1, sum += tmp;
    			if(out[i] > in[i]) AddEdge(s, i, tmp);
    			else AddEdge(i, t, tmp);
    		}
    	if(ok == 1) return true;
    	else if(ok == -1) return false;
    	return maxflow() == sum / 2; 
    }
    
    void dfs(int u)
    {
    	REP(v, 0, n)
    		if(map[u][v] && vis[u][v] > 0) //注意vis的用法,为避免重边和自环 
    		{
    			vis[u][v]--;
    			dfs(v);
    			path.push_back(v + 1);
    		}
    }
    
    void print()
    {
    	REP(i, 0, edges.size())
    	{
    		Edge& e = edges[i];
    		if(e.from != s && e.from != t  && e.to != s && e.to != t && e.cap != 0) //注意是非反向弧 
    		{
    			if(e.flow == 1) map[e.to][e.from] = 1, vis[e.to][e.from]++;
    			else map[e.from][e.to] = 1, vis[e.from][e.to]++;
    		}
    	}
    	
    	dfs(0);
     	printf("1");
      	for(int i = path.size()-1; i >= 0; i--) printf(" %d", path[i]);
    	puts("");
    }
    
    void init()
    {
    	edges.clear(); path.clear();
    	REP(i, 0, MAXN) g[i].clear();
    	memset(in, 0, sizeof(in));
    	memset(out, 0, sizeof(out));
    	memset(map, 0, sizeof(map));
    	memset(vis, 0, sizeof(vis));
    }
    
    int main()
    {
    	int T;	
    	scanf("%d", &T);
    
    	while(T--)
    	{
    		init();
    		scanf("%d%d", &n, &m);
    		s = n; t = s + 1;
    		
    		while(m--)
    		{
    			int u, v;
    			char p[2];
    			scanf("%d%d%s", &u, &v, p);
    			u--; v--;
    			out[u]++; in[v]++;
    			if(p[0] == 'U') AddEdge(u, v, 1);
    			else map[u][v] = 1, vis[u][v]++;
    		}
    		
    		if(!judge()) puts("No euler circuit exist");
    		else print();
    		if(T) puts("");
    	}
    	
    	return 0;	
    } 

  • 相关阅读:
    java 第三次实验作业关于封装
    java String类实验作业随笔
    java 第一次基础实践
    英语四级冲刺笔记---谓语动词的时态下
    英语四级冲刺笔记——语法篇二
    英语四级冲刺笔记——语法篇一
    java面向对象---对象容器
    java--面向对象---访问属性
    java面向对象---对象初始化
    java面向对象---成员变量和成员函数
  • 原文地址:https://www.cnblogs.com/sugewud/p/9819525.html
Copyright © 2011-2022 走看看