P4016 负载平衡问题

(color{#0066ff}{题目描述})

G 公司有 n 个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等。

如何用最少搬运量可以使 n 个仓库的库存数量相同。

搬运货物时,只能在相邻的仓库之间搬运。

(color{#0066ff}{输入格式})

文件的第 1 行中有 1 个正整数 n,表示有 n 个仓库。

第 2 行中有 n 个正整数,表示 n 个仓库的库存量。

(color{#0066ff}{输出格式})

输出最少搬运量。

(color{#0066ff}{输入样例})

5
17 9 14 16 4

(color{#0066ff}{输出样例})

11

(color{#0066ff}{题解})

这道题有两种做法

1、贪心

首先,最终,每个仓库的库存一定是平均数。

而且,这是个环形的仓库集合,每个仓库向相邻仓库传递货物

但是这样并不方便维护,我们考虑整体和隔离的思想。将前i个看做一个整体,显然前i个内部的均分是不会改变其整体结构的,因而对于该体系来说,想要达到平均数结构,就必须与下一个体系交换足够的纸牌,而交换数量就是(|G[i] - i * ive|)其中G[i]是前缀和。然后就可以推出一个结论:(d = sum^M _{i = 1}|i*ave−G[i] |),也就是将每次体系更新的贡献加起来。

(b_i)代表库存量,(ave=frac{sum_{i=1}^{n}b_i}{n}),即平均数,令(a_i=b_i-ave,s_i)(a_i)的前缀和

则在1-n排成一排的情况下(ans=sum{|s_i|})

如果在第k个人后断环为链

则差及其前缀和分别为

(a_{k+1},s_{k+1}-s_k)

(a_{k+2},s_{k+2}-s_k)

(a_{k+3},s_{k+3}-s_k)

(...................)

(a_{n},s_{n}-s_k)

(a_{1},s_{1}+s_n-s_k)

(a_{2},s_{2}+s_n-s_k)

(...................)

(a_{k},s_{k}+s_n-s_k)

因为在最终时,一定有(s_n=0)

(注意,(s_i eq 0))

所以代价即为(sum{|s_i-s_k|})

因此选取中位数(s_k)即可求出最小代价

#include<cstdio>
#include<queue>
#include<vector>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<cmath>
#define _ 0
#define LL long long
#define Space putchar(' ')
#define Enter putchar('
')
#define fuu(x,y,z) for(int x=(y);x<=(z);x++)
#define fu(x,y,z)  for(int x=(y);x<(z);x++)
#define fdd(x,y,z) for(int x=(y);x>=(z);x--)
#define fd(x,y,z)  for(int x=(y);x>(z);x--)
#define mem(x,y)   memset(x,y,sizeof(x))
#ifndef olinr
inline char getc()
{
	static char buf[100001],*p1=buf,*p2=buf;
	return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100001,stdin),p1==p2)? EOF:*p1++;
}
#else
#define getc() getchar()
#endif
template<typename T>inline void in(T &x)
{
	int f=1; char ch; x=0;
	while(!isdigit(ch=getc()))(ch=='-')&&(f=-f);
	while(isdigit(ch)) x=x*10+(ch^48),ch=getc();
	x*=f;
}
int n,a[150],s[150],ave,ans;
int main()
{
	in(n);
	fuu(i,1,n) in(a[i]),ave+=a[i];
	ave/=n;
	fuu(i,1,n) s[i]=s[i-1]+(a[i]-ave);
	std::sort(s+1,s+n+1);
	fuu(i,1,n) ans+=std::abs(s[i]-s[(n+1)>>1]);
	printf("%d",ans);
	return ~~(0^_^0);
}

2、最小费用最大流

建立超级源s,超级汇t

对于(a_i)

若它小于(ave),则从s向其连一条容量为(|a_i-ave|)(他到平均值需要这么多),费用为0(只是起连接作用,并不是传递)的边

若它大于(ave),则从其向t连一条容量为(|a_i-ave|),费用为0的边

而且,所有相邻的货仓(包括1和n)之间连容量为inf(相邻无限传递),费用为1(代价为1)的边

从s到t跑最大流即可

#include<cstdio>
#include<queue>
#include<vector>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<cmath>
#define _ 0
#define LL long long
#define Space putchar(' ')
#define Enter putchar('
')
#define fuu(x,y,z) for(int x=(y);x<=(z);x++)
#define fu(x,y,z)  for(int x=(y);x<(z);x++)
#define fdd(x,y,z) for(int x=(y);x>=(z);x--)
#define fd(x,y,z)  for(int x=(y);x>(z);x--)
#define mem(x,y)   memset(x,y,sizeof(x))
#ifndef olinr
inline char getc()
{
	static char buf[100001],*p1=buf,*p2=buf;
	return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100001,stdin),p1==p2)? EOF:*p1++;
}
#else
#define getc() getchar()
#endif
template<typename T>inline void in(T &x)
{
	int f=1; char ch; x=0;
	while(!isdigit(ch=getc()))(ch=='-')&&(f=-f);
	while(isdigit(ch)) x=x*10+(ch^48),ch=getc();
	x*=f;
}
struct node
{
	int to;
	int nxt;
	int dis,cap,flow;
}e[550000];
int head[555],dis[555],change[555],road[555];
bool vis[555];
int a[555];
const int inf=0x7fffffff;
int s,t;
int n;
int cnt=1;
std::queue<int> q;
inline void add(int from,int to,int dis,int cap)
{
	cnt++;
	e[cnt].to=to;
	e[cnt].dis=dis;
	e[cnt].cap=cap;
	e[cnt].flow=0;
	e[cnt].nxt=head[from];
	head[from]=cnt;
}
inline bool spfa()
{
	fuu(i,0,n+1) change[i]=inf,dis[i]=inf;
	q.push(s);
	dis[s]=0;
	while(!q.empty())
	{
		int tp=q.front();
		q.pop();
		vis[tp]=false;
		for(int i=head[tp];i;i=e[i].nxt)
		{
			int go=e[i].to;
			if(dis[go]>dis[tp]+e[i].dis&&e[i].cap>e[i].flow)
			{
				change[go]=std::min(change[tp],e[i].cap-e[i].flow);
				road[go]=i;
				dis[go]=dis[tp]+e[i].dis;
				if(!vis[go])
				{
					vis[go]=true;
					q.push(go);
				}
			}
		}
	}
	return change[t]!=inf;
}
inline void mcmf()
{
	LL cost=0;
	while(spfa())
	{
		cost+=change[t]*dis[t];
		for(int o=t;o!=s;o=e[road[o]^1].to)
		{
			e[road[o]].flow+=change[t];
			e[road[o]^1].flow-=change[t];
		}
	}
	printf("%lld",cost);
}
int main()
{
	in(n);
	int ave=0;
	fuu(i,1,n) in(a[i]),ave+=a[i];
	ave/=n;
	s=0,t=n+1;
	fuu(i,1,n)
	{
		add(i,(i%n)+1,1,inf);
		add((i%n)+1,i,-1,0);
		add(i,i-1? i-1:n,1,inf);
		add(i-1? i-1:n,i,-1,0);
		if(a[i]<ave) add(s,i,0,ave-a[i]),add(i,s,0,0);
		if(a[i]>ave) add(i,t,0,a[i]-ave),add(t,i,0,0);
	}
	mcmf();
	return ~~(0^_^0);
}
原文地址:https://www.cnblogs.com/olinr/p/9975585.html