【网络流】【图论】【模板】网络最大流(luogu P3376)

≡放荡痞女 提交于 2019-12-28 10:04:09

前言

在这里插入图片描述在这里插入图片描述
我枯了


题目:

在这里插入图片描述

数据:

在这里插入图片描述


思路:

先讲一些概念性的定义

名称 定义
原点 只有流出的点
汇点 只有流入的点
流量 一条边流过的量
容量 一条边的最大流量
残量 容量 - 流量

然后在看基本性质
1.对于任意一个“管道”流量 <=<= 容量 这个就很显然没什么好说
2.每个点(除原点和汇点)的入流和出流相等,就是原点流出多少就会有多少流入汇点,这个也很显然
3.对于一条有向边(u,v)k[u][v] = -k[v][u]
其实就是在这里插入图片描述

最后来看一下算法
我们只学了dinic和一种慢到炸的算法
显然用dinic,毕竟快
这个算法就是每次以原点bfs求出每个边的编号,表示从原点到i通过至少几条残量大于0的边,再跑dfs找增广路并增广,如果没有增广路,返回步骤一,如果bfs没到汇点证明算法结束


CodeCode:

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

const long long INF =1<<29;


using namespace std;

long long n, m, s, y, t;
long long h[100000],dis[100000];
long long tt;
queue<long long>hy;


struct node
{
	long long y,w,to,next;
}e[500001];
long long read() //快读然并卵
{
	long long x=0,flag=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{if(ch=='-')flag=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')
	{x=x*10+ch-'0';ch=getchar();}
	return x*flag;
}
void add (long long x, long long y, long long z)//加边
{
	e[++tt] = (node){y, z, tt + 1, h[x]} ; h[x] = tt;//正向
	e[++tt] = (node){x, 0, tt - 1, h[y]} ; h[y] = tt;//反向
}
void input ()//输入
{
	n = read (); m = read ();  s = read (); t = read ();
	for (long long i = 1; i <= m; ++i)
	{
		long long x, y, z;
		x=read (); y= read (); z = read ();
		add (x, y, z);//建图
	}
} 
bool bfs ()//bfs判断
{
	
    memset(dis,0x7f,sizeof(dis));//初始化
    dis[s] = 0;
    hy.push(s);
	while (hy.size ())//bfs
	{
		long long x = hy.front ();
		hy.pop ();
		for (long long i = h[x]; i; i = e[i].next)//搜相邻的边
		{
			long long y = e[i].y;
			if (dis[y] > dis[x] + 1 && e[i].w)
			{
			    dis[y] = dis[x] + 1;
				if (y == t) return 1;//找到
				hy.push (y);	
			}
		}
		 
	}
	return 0;//没有
}
long long dfs (long long dep,long long diss)//找增广路
{
	if (dep == t) return diss;
	long long now_liuliang = 0;//当前流量
	for (long long i = h[dep]; i; i = e[i].next)
	{
		long long y =e[i].y;
	 	if (dis[y] == dis[dep] + 1 && e[i].w)
	    {
	    	long long f = dfs (y, min(e[i].w, diss - now_liuliang));
	    	if (!f) dis[y] = -1;//打标记
	    		
			e[i].w -= f;//当前 - 流过
	    	e[e[i].to].w += f;//反向 + 流过
	        now_liuliang += f;//正向 + 流过
			if (now_liuliang == diss)break;			
	    	
	    }	
	    
	}
	return now_liuliang;//返回
}
long long dinic ()//dinic
{
	long long answer = 0;
	while(bfs()) answer += dfs (s,INF);//能增广就增广
	return answer ; 
}
int main ()
{
	input ();
	printf("%lld",dinic());
	return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!