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 \neq 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('\n')
#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('\n')
#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);
}