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

14 篇文章 1 订阅
1 篇文章 0 订阅

前言

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


题目:

在这里插入图片描述

数据:

在这里插入图片描述


思路:

先讲一些概念性的定义

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

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

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


C o d e Code Code:

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值