zoukankan      html  css  js  c++  java
  • 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);
    }
    
  • 相关阅读:
    单例实现c++
    c++智能指针实现方式1
    c++中处理输入输出的方法
    makefile函数
    5. Longest Palindromic Substring
    go 语言中常用的包
    ubuntu14.04 boost动态库找不到 libboost_system.so.1.58.0
    boost-asio-cpp-network-programming阅读笔记
    链接-装载-库,读书笔记
    leecode第二百一十七题(存在重复元素)
  • 原文地址:https://www.cnblogs.com/olinr/p/9975585.html
Copyright © 2011-2022 走看看