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);
    }
    
  • 相关阅读:
    sqlserver中判断表或临时表是否存在
    Delphi 简单方法搜索定位TreeView项
    hdu 2010 水仙花数
    hdu 1061 Rightmost Digit
    hdu 2041 超级楼梯
    hdu 2012 素数判定
    hdu 1425 sort
    hdu 1071 The area
    hdu 1005 Number Sequence
    hdu 1021 Fibonacci Again
  • 原文地址:https://www.cnblogs.com/olinr/p/9975585.html
Copyright © 2011-2022 走看看