网络流24题 洛谷 4016 负载平衡问题

本文介绍如何使用网络流算法解决仓库货物均值分配问题。通过构建特定图结构,并运用SPFA算法寻找最短增广路径,实现最小搬运成本下的货物均值分配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

重在建图

第一直觉是入门题目分纸牌,那道题是贪心的。

但是网络流标签嘛就用网络流做了。


建图:(流量,费用)


1、超级源点 src + 超级汇点 sink

2、对于仓库 i,拆成 Ai 和 Bi 两个点

  2 . 1、从 Ai 向 Bi 建立 (inf,0)

  2 . 2、从 Bi 向 Ai-1 和 Ai+1 建边 (inf,1)

3、超级源点 和 Ai 建边 (库存,0)

4、Bi 和 超级汇点 建边 (avr,0)->(((avr表示average)))


正确性:


由于费用流遵循在有流量的边中找最短路,那么它最开始“填掉”的边一定是费用为0的(如果本身有能力“填掉”的话),也就是 src->Ai->Bi ->sink 路径上的边;但如果有些仓库自身无法将均值边的流量“填掉”,也就是图依旧可以增广,那么就必须从其他地方运过来,那么就要经过费用为1的 Bi->Aj 的边,这将产生费用,由于是 spfa 最短路,那么当前求得的路径一定是最小的搬运费用。

增广终止的条件一定是 avr 的流量全部流满,也就是每个仓库最后达到均值的情况;否则一直增广,相当于一直搬运!

代码:

#include <bits/stdc++.h>

const  int  N = 1000 + 5 ;

std :: queue < int >  q ;

int  head [ N ] , nxt [ N ] , dis [ N ] , flt [ N ] , to [ N ] , cn = 1 ;
int  pree [ N ] , pred [ N ] , c [ N ] ;
int  src , sink , inf , n , m , x , mincost , avr ;
bool  vis [ N ] ; 

void  create ( int  u , int  v , int  f , int  d ) {
	cn ++ ;
	to [ cn ] = v ;
	dis [ cn ] = d ;
	flt [ cn ] = f ;
	nxt [ cn ] = head [ u ] ;
	head [ u ] = cn ;
	
	cn ++ ;
	to [ cn ] = u ; 
	dis [ cn ] = - d ;
	flt [ cn ] = 0 ;
	nxt [ cn ] = head [ v ] ;
	head [ v ] = cn ; 
} 

void  prp ( ) {
	int  o [ 5 ] ;
	memset ( o , 127 , sizeof ( o ) ) ;
	inf =  o [ 0 ] ;
}

bool  spfa ( ) {
	memset ( vis , false , sizeof ( vis ) ) ;
	memset ( c , 127 , sizeof ( c ) ) ;
	q . push ( src ) ;
	c [ src ] = 0 ; vis [ src ] = true ;
	while ( ! q . empty ( ) ) {
		int  tmp = q . front ( ) , v ;
		q . pop ( ) ; vis [ tmp ] = false ;
		for ( int  i = head [ tmp ] ; i ; i = nxt [ i ] ) {
			v = to [ i ] ;
			if ( flt [ i ]  &&  c [ v ] > c [ tmp ] + dis [ i ] ) {
				c [ v ] = c [ tmp ] + dis [ i ] ;
				pree [ v ] = i ;
				pred [ v ] = tmp ; 
				if ( ! vis [ v ] ) {
					q . push ( v ) ;
					vis [ v ] = true ; 
				}
			}
		}
	}
	return  c [ sink ] < inf ;
}

void  augment ( ) {
	int  u = sink , delta = inf ;
	while ( u != src ) {
		if ( delta > flt [ pree [ u ] ] )
			delta = flt [ pree [ u ] ] ;
		u = pred [ u ] ;
	}
	u = sink ;
	while ( u != src ) {
		flt [ pree [ u ] ] -= delta ;
		flt [ pree [ u ] ^ 1 ] += delta ;
		u = pred [ u ] ;
	}
	mincost += delta * c [ sink ] ; 
}


int  main ( ) {
	scanf ( "%d" , & n ) ;
	prp ( ) ;
	src = 0 ; sink = 2 * n + 1 ;
	for ( int  i = 1 ; i <= n ; i ++ ) {
		scanf ( "%d" , & x ) ;
		create ( src , i , x , 0 ) ;
		avr += x ;
	}
	avr /= n ;
	
	for ( int  i = 1 ; i <= n ; i ++ )
		create ( i , i + n , inf , 0 ) ;
	for ( int  i = n + 2 ; i < 2 * n ; i ++ )
		create ( i , i - n - 1 , inf , 1 ) , create ( i , i - n + 1 , inf , 1 ) ;
	create ( n + 1 , 2 , inf , 1 ) , create ( n + 1 , n , inf , 1 ) ;
	create ( n + n , 1 , inf , 1 ) , create ( n + n , n - 1 , inf , 1 ) ;
	
	for ( int  i = n + 1 ; i <= n + n ; i ++ )
		create ( i , sink , avr , 0 ) ;
	while ( spfa ( ) )
		augment ( ) ;
	printf ( "%d" , mincost ) ;
	return  0 ;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值