图论 - 网络流_最大流入门题

筅森魡賤 提交于 2019-11-26 18:17:57

照着刘汝佳的训练指南,网络流从开始学习到刷了10来题,共花了10天,其中不知道为什么有好几天的断档。。

网络流,目前我就用着dinic算法,一开始是用的EK算法,即普通的增广路算法。本来还打算搞下ISAP算法,不过听铭神说这两种算法他在做题时,一般ISAP能过的,Dinic一样过了。so,那就算了,暂时先放放,时间也不多。

————————————————————————————————————————————————

题目:

http://vjudge.net/contest/view.action?cid=48586#overview

上面的链接是我挂在虚拟OJ上的contest,都是从下面网络流题集中调出来一些的。

网络流的题集:http://blog.csdn.net/shahdza/article/details/7779537 此处很齐全。


体会:

网络流主要注重你的构图能力,一开始还以为最大流的算法会很难的,其实还可以接受。

感觉以后刷专题要写博客的话,还是A一题写一篇,到最后再整合。一次写10多题的题解,好累。。。

————————————————————————————————————————————————

A - PIGS

poj 1149

一开始不会,后来看了看题解,神一般的分析,代码就不敲了。

http://www.cnblogs.com/sleeper-qp/archive/2012/07/23/2605253.html

B - Drainage Ditches 

POJ 1273 / HDU 1532

模板题,不过会有重边,注意一下。

*我一开始用EK算法敲过了,不知为何,后来学会用Dinic算法敲就不对。后来还没找到错误,先mark一下。

EK算法(邻接矩阵):

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#include <iostream>
#define debug cout<<"??"<<endl;
using namespace std;
const int INF = 10000005;
const int SIZE = 205;
int n,m;
int a[SIZE],pre[SIZE];//a == vis;pre record its parent node
int flow[SIZE][SIZE];
int G[SIZE][SIZE];

int deal()
{
	queue <int> q;
	int f = 0;
	for(;;)
	{
		memset(a,0,sizeof(a));
		//memset(pre,0,sizoef(0));
		a[1] = INF;
		pre[1] = -1;
		q.push(1);
		while(!q.empty())
		{
			int u = q.front();
			q.pop();
			for(int i = 1;i <= m;i++)
			{
				if(!a[i] && G[u][i] > flow[u][i])
				{

					q.push(i);
					a[i] = min(a[u],G[u][i] - flow[u][i]);
					pre[i] = u;
				}
			}
		}
		if(a[m] == 0)	break;
		int t = m;
		while(t != -1)
		{
			flow[pre[t]][t] += a[m];//正向
			flow[t][pre[t]] -= a[m];//逆向
			t = pre[t];
		}
		f += a[m];
	}
	return f;
}
int main()
{
	while(scanf("%d%d",&n,&m) != EOF)
	{
		memset(G,0,sizeof(G));		//上限
		memset(flow,0,sizeof(flow));//flow 为当前管道已经占用的容量
		int u,v,cap;
		for(int i = 0;i < n;i++)
		{
			scanf("%d%d%d",&u,&v,&cap);
			G[u][v] += cap;
		}
		printf("%d\n",deal());
	}
	return 0;
}</span>

C - Power Network

POJ 1459

题意描述有点不清晰,容易让人理解错误。不过后来直接看例子就懂了。

power station :供能

consumer:耗能

dispatcher:转发(普通点)

自己创建一个s点,连到每个power station上,容量为power station的供能能力。

创建一个t点,每个consumer连到t点,容量为consumer的消耗能力。

*自己连到自己的边,可以忽略掉

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <queue>
#include <iostream>
#include <cmath>
using namespace std;
#define debug cout<<"debug"<<endl;
const int MAXN = 110;
const int MAXEDGE = 21000;
const int INF = 0x7ffffff;
struct Edge
{
	int to,cap,flow,next;
};
struct Dinic
{
	int m, s, t;
	Edge edge[MAXEDGE];
	int head[MAXN], d[MAXN], cur[MAXN];
	bool vis[MAXN];
	Dinic(int s, int t)
	{
		this -> s = s,this -> t = t;
		memset(head, -1, sizeof(head));
		m = 0;
	}
	void AddEdge(int u,int v, int c)
	{
		edge[m] = (Edge){v, c, 0,head[u]};
		head[u] = m++;

		edge[m] = (Edge){u, 0, 0,head[v]};
		head[v] = m++;
	}
	bool bfs()
	{
		memset(vis,false,sizeof(vis));
		d[s] = 0;
		vis[s] = true;
		queue <int> q;
		q.push(s);
		while(!q.empty())
		{
			int u = q.front();
			int next = head[u];
			q.pop();
			while(next != -1)
			{
				Edge &e = edge[next];
				if(!vis[e.to] && e.cap > e.flow)
				{
					vis[e.to] = true;
					q.push(e.to);
					d[e.to] = d[u] + 1;
				}
				next = e.next;
			}
		}
		return vis[t];
	}
	int dfs(int x, int a)
	{
		if(x == t || a == 0)	return a;
		int flow = 0, f;
		int& next = cur[x];
		while(next != -1)
		{
			Edge& e = edge[next];
			if(d[e.to] == d[x] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0)
			{
				e.flow += f;
				edge[next^1].flow -= f;
				flow += f;
				a -= f;
				if(a == 0)	break;	//直到当前节点的最大残量若为0,则说明前面存在某条边已达上限
			}
			next = e.next;
		}
		return flow;
	}
	int getMaxFlow(int n)
	{
		int f = 0;
		while(bfs())
		{
			for(int i = 0;i < n+2; i++)
                cur[i] = head[i];
			f += dfs(s, INF);
		}
		return f;
	}
};

int main()
{
	int n,np,nc,m;
	while(scanf("%d %d %d %d",&n, &np, &nc, &m) != EOF)
	{
		int u, v, c;
		int s = n, t = n + 1;
		Dinic dinic(s, t);
		for(int i = 0;i < m; i++)
		{
			scanf(" (%d,%d)%d",&u,&v,&c);
			if(u == v)	continue;
			dinic.AddEdge(u,v,c);
		}
		for(int i = 0;i < np; i++)
		{
			scanf(" (%d)%d",&v,&c);
			dinic.AddEdge(s, v, c);
		}
		for(int i = 0;i < nc; i++)
		{
			scanf(" (%d)%d",&u,&c);
			dinic.AddEdge(u, t, c);
		}
		printf("%d\n",dinic.getMaxFlow(n));
	}
	return 0;
}</span>

D - Optimal Milking

POJ 2112

不会= =, 直说了。后来看了题解,二分+floyd+最大流。。。主要对floyd以及二分不熟悉,因此暂时先搁着。

http://www.cppblog.com/abilitytao/archive/2010/11/06/132666.html

E - Dining

POJ 3281

主要是构图。拆点,把牛拆成两个点。用容量为1的边相连(实际图中每条边的容量都是1)

构图方式:S —— 食物food——牛——牛——饮料drink——T

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <queue>
#define debug cout<<"debug"<<endl;
using namespace std;
const int MAXPOINT = 505;
const int MAXEDGE = 300050;
const int INF = 1;
struct Edge
{
	int to, cap, flow, next;
};
Edge edge[MAXEDGE];
struct Dinic
{
	int n, m, s, t;
	int cur[MAXPOINT], d[MAXPOINT];
	bool vis[MAXPOINT];
	int head[MAXPOINT];
	Dinic(int n, int s, int t)
	{
		m = 0;
		memset(head, -1, sizeof(head));
		this->n = n, this->s = s,this->t = t;
	}
	void AddEdge(int u, int v, int cap)
	{
		edge[m] = (Edge){v, cap, 0, head[u]};
		head[u] = m++;

		edge[m] = (Edge){u, 0, 0,head[v]};
		head[v] = m++;
	}
	int getMaxFlow()
	{
		int f = 0;
		while(bfs())
		{
			for(int i = 0;i < n; i++)
				cur[i] = head[i];
			f += dfs(s, INF);
		}
		return f;
	}
	bool bfs()
	{
		memset(vis, false,sizeof(vis));
		d[s] = 0, vis[s] = true;
		queue <int> q;
		q.push(s);
		while(!q.empty())
		{
			int u = q.front();
			q.pop();
			int next = head[u];
			while(next != -1)
			{
				Edge&e = edge[next];
				if(!vis[e.to] && e.cap > e.flow)
				{
					vis[e.to] = true;
					q.push(e.to);
					d[e.to] = d[u] + 1;
				}
				next = e.next;
			}
		}
		return vis[t];
	}
	int dfs(int u, int a)
	{
		if(u == t || a == 0)	return a;
		int flow = 0, f;
		int& next = cur[u];
		while(next != -1)
		{
			Edge& e = edge[next];
			if(d[e.to] == d[u] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0)
			{
				e.flow += f;
				edge[next^1].flow -= f;

				a -= f;
				flow += f;
				if(a == 0)	break;
			}
			next = e.next;
		}
		return flow;
	}
	void display(int n)
	{
	    int next = head[n];
	    while(next != -1)
        {
            printf("%d ",edge[next].to);
            next =edge[next].next;
        }
        puts("");
	}
};
int main()
{
	int n, f, d;
	while(scanf("%d%d%d", &n, &f, &d) != EOF)
	{
		int s = 0, t = f+d+2*n+1;
		Dinic dinic(t+1, s, t);
		//s to food
		for(int i = 1;i <= f; i++)
        {
			dinic.AddEdge(s, i, 1);
        }
		//cow1 to cow2
		for(int i = 1;i <= n; i++)
		{
			dinic.AddEdge(f+i, f+n+i, 1);
		}

		int nf, nd, tmp;
		for(int i = 1;i <= n; i++)
		{
			scanf("%d%d", &nf, &nd);
			//food to cow1
			for(int j = 0;j < nf; j++)
			{
				scanf("%d",&tmp);
				dinic.AddEdge(tmp, f+i, 1);
			}
			//int num = n+f+1;
			//cow2 to drink
			for(int j = 0;j < nd; j++)
			{
				scanf("%d", &tmp);
				dinic.AddEdge(f+n+i, f+2*n+tmp, 1);
			}
		}
		//cow2 to t
		for(int i = f+2*n+1;i <= f+2*n+d; i++)
		{
			dinic.AddEdge(i, t, 1);
		}
		printf("%d\n",dinic.getMaxFlow());
	}
	return 0;
}</span>

G - Flow Problem

HDU 3549

原来还有一题是用EK算法的。直接套模板就好了。

H - Task Schedule

HDU 3572

构图思想很重要。

S点连到每个任务,容量完成任务所需要的天数。

把每个任务连接到在天数完成的范围,容量均为1。例如要求第2天开始,第7天前要完成。那么,该任务就连到2,3,4...7天的结点。

每个天结点均连到T点,容量为机器的数量。

I - Leapin' Lizards

HDU 2732

还是拆点。

1.首先处理哪些柱子可以直接跳出所在的room,则把这些柱子连到t点。

2.s点连到每根有蜥蜴的柱子上,容量为1。

3.柱子本身拆成两个点,连一条边。容量为:如果上面已经有蜥蜴了,则流量为原流量减 1,否则为原流量大小。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <queue>
#include <vector>
#include <cmath>
#define cal(x1, y1, x2, y2)	(x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)
using namespace std;
const int MAXROW = 25;
const int MAXPOINT = 1005;
const int MAXEDGE = 500005;
char m1[MAXROW][MAXROW],m2[MAXROW][MAXROW];
const int INF = 10000000;
#define debug cout<<"debug"<<endl;
struct Edge
{
	int to, cap, flow, next;
};
struct Point
{
	int b1,b2;
};
Edge edge[MAXEDGE];
Point xb[MAXROW][MAXROW];
struct Dinic
{
	int m, s, t;            //m: the quantity of edge
	int cur[MAXPOINT], head[MAXPOINT], d[MAXPOINT];
	bool vis[MAXPOINT];
	Dinic()
	{
		m = 0;
		memset(head, -1, sizeof(head));
	}
	void init(int s, int t)
	{
		this -> s = s,this -> t = t;
	}
	void AddEdge(int u, int v, int cap)
	{
		edge[m] = (Edge){v, cap, 0, head[u]};
		head[u] = m++;

		edge[m] = (Edge){u, 0, 0,head[v]};
		head[v] = m++;
	}
	bool bfs()
	{
		memset(vis, false,sizeof(vis));
		queue <int> q;
		q.push(s);
		vis[s] = true, d[s] = 0;
		while(!q.empty())
		{
			int u = q.front();
			int next = head[u];
			q.pop();
			while(next != -1)
			{
				Edge& e = edge[next];
				int v = e.to;
				if(!vis[v] && e.cap > e.flow)
				{
					vis[v] = true;
					q.push(v);
					d[v] = d[u] + 1;
				}
				next = e.next;
			}
		}
		return vis[t];
	}
	int dfs(int u, int a)
	{
		if(u == t || a == 0)	return a;

		int& next = cur[u];
		int flow = 0, f;
		while(next != -1)
		{
		    Edge& e = edge[next];
			int v = e.to;
			if(d[v] == d[u] + 1 && (f = dfs(v, min(a, e.cap - e.flow))) > 0)
			{
				e.flow += f;
				edge[next^1].flow -= f;

				flow += f;
				a -= f;
				if(a == 0)	break;
			}
			next = e.next;
		}
		return flow;
	}
	int getMaxFlow(int cot)
	{
		int f = 0;
		while(bfs())
		{
			for(int i = 0;i < cot; i++)
				cur[i] = head[i];
			f += dfs(s, INF);
		}
		return f;
	}
};
void input(int n)
{
	for(int i = 0;i < n; i++)
	{
		scanf("%s", m1[i]);
	}
	for(int i = 0;i < n; i++)
	{
		scanf("%s", m2[i]);
	}
}
int main()
{
	int t;
	scanf("%d",&t);
	int cas = 0;
	while(t--)
	{

		int n, dis;
		scanf("%d%d",&n, &dis);
		getchar();
		input(n);
		//build Graph
		int lizard = 0;
		int len = strlen(m1[0]);
		int cot = 0;	//
		Dinic dinic;
		for(int i = 0;i < n; i++)			//³õʼ»¯±êºÅ
		{
			for(int j = 0;j < len; j++)
			{
				int num = m1[i][j] - '0';
				if(num == 0)	continue;

				Point& e = xb[i][j];
				e.b1 = cot++;
				e.b2 = cot++;
				if(m2[i][j] == 'L')
				{
				    lizard++;
					dinic.AddEdge(e.b2, e.b1, num-1);
					//printf("%d %d %d",e.b2, e.b1, num-1);
				}
				else
				{
					dinic.AddEdge(e.b2, e.b1, num);
				}
			}
		}
		int s = cot, t = cot + 1;
		dinic.init(s, t);
		for(int i = 0;i < n; i++)
		{
			for(int j = 0;j < len; j++)
			{
				int num = m1[i][j] - '0';
				if(num == 0)	continue;

				//point to t;
				Point& e = xb[i][j];
				if(i+dis > n-1 || j+dis > len-1 || i - dis < 0 || j - dis < 0)
				{
					dinic.AddEdge(e.b1, t, num);
					//cout<<e.b1<<" "<<num<<endl;
				}

				//ponit to s;
				if(m2[i][j] == 'L')
				{
					dinic.AddEdge(s, e.b1, 1);
				}
				//normal point to normal point

				for(int t = max(0, i-dis); t < min(n, i + dis + 1); t++)
				{
					for(int p = max(0, j-dis); p < min(len, j + dis + 1); p++)
					{
						if((i == t && j == p) || m1[t][p] == '0')	continue;
						if(cal(i, j, t, p) > dis*dis)	continue;
						dinic.AddEdge(e.b1, xb[t][p].b2, INF);
					}
				}
			}
		}
		int res = lizard - dinic.getMaxFlow(cot+2);
		if(res == 0)
			printf("Case #%d: no lizard was left behind.",++cas);
		else if(res == 1)
			printf("Case #%d: 1 lizard was left behind.",++cas);
		else
			printf("Case #%d: %d lizards were left behind.",++cas,res);
        puts("");
	}
	return 0;
}</span>

J - Pahom on Water

HDU 4183

一开始也没什么思路,看了别人题解后(http://blog.csdn.net/u011686226/article/details/25245465),思路完全通了。

K - Route Redundancy

HDU 4240

这题的描述更是让人困惑。其实就是叫你求出最大流,然后除以某条路径中最大的流量。

在dfs里面判断一下,保存这个最大的流量。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <queue>
using namespace std;
const int MAXPOINT = 1005;
const int MAXEDGE = 3000500;
const int INF = 0x7FFFFFF;
struct Edge
{
	int to, cap, flow, next;
};
Edge edge[MAXEDGE];
struct Dinic
{
	int n, m, s, t;
	int srmaxflow;
	bool vis[MAXPOINT];
	int cur[MAXPOINT], head[MAXPOINT];
		//single route max flow;
	int d[MAXPOINT];	//distanct
	Dinic(int n, int s, int t)
	{

		this -> n = n;
		this -> s = s;
		this -> t = t;
		srmaxflow = m = 0;
		memset(head, -1, sizeof(head));
	}
	void AddEdge(int u, int v, int cap)
	{
		edge[m] = (Edge){v, cap, 0, head[u]};
		head[u] = m++;

		edge[m] = (Edge){u, 0, 0, head[v]};
		head[v] = m++;
	}
	bool bfs()
	{
		memset(vis, false, sizeof(vis));
		queue <int> q;
		q.push(s);
		vis[s] = true;
		while(!q.empty())
		{
			int u = q.front();
			q.pop();
			int next = head[u];
			while(next != -1)
			{
			    Edge& e = edge[next];
				if(!vis[e.to] && e.cap > e.flow)
				{
					vis[e.to] = true;
					d[e.to] = d[u] + 1;
					q.push(e.to);
				}
				next = e.next;
			}
		}
		return vis[t];
	}
	int dfs(int u, int a)
	{
		if(u == t || a == 0)	return a;
		int& next = cur[u];
		int flow = 0, f;
		while(next != -1)
		{
		    Edge& e = edge[next];
			if(d[e.to] == d[u] + 1 && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0)
			{
				e.flow += f;
				edge[next^1].flow -= f;
				srmaxflow = max(srmaxflow, f);
				a -= f;
				flow += f;
				if(a == 0)	break;
			}
			next = e.next;
		}
		return flow;
	}
	float getMaxFlow()
	{
		float f = 0;
		while(bfs())
		{
			for(int i = 0;i < n; i++)
				cur[i] = head[i];
			f += dfs(s, INF);
		}
		//if(srmaxflow == 0)	return 0;
		return f/srmaxflow;
	}
};
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int d, n, e, s, t;
		int u, v, cap;
		scanf("%d%d%d%d%d",&d, &n, &e, &s, &t);
		Dinic dinic(n, s, t);
		for(int i = 0;i < e; i++)
		{
			scanf("%d%d%d", &u, &v, &cap);
			dinic.AddEdge(u, v, cap);
		}
		printf("%d %.3f\n",d, dinic.getMaxFlow());
	}
	return 0;
}</span>


L - Marriage Match II

HDU 3081

这题告诉我,求最大流时是不应该修改图的任何东西。任何求最大流的问题,总是先构好图,然后跑一遍最大流即得出结果。要改也要等当前的最大流跑完算法后再改。

http://blog.csdn.net/qq564690377/article/details/7857983



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!