P4016 负载平衡问题

题目

题目

思路

《惊!网络瘤24题里有dp》——OI日报
小学5年级时,这个拆环dp用时1周,如今竟是网络瘤,是我太年轻
垂死病中惊坐起,小丑竟是我自己
我们考虑建2个新点s和t,对于每一个仓库,如果该运出就从s连一条容量为运出数量,费用0的边,该运入就向t连一条容量为运入数量,费用0的边,仓库之间按题意,连容量inf,费用1的双向边
接下来就是最小费用最大流。
code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
int n,m,s,t,x,y,tot=2,head[5001],dep[5001],fy;
bool book[5001];
long long w,ans,in[5001],ans2;
long long mn(long long x,long long y)
{
 return (x>y?y:x);
}
struct f{
 int to,net;
 long long w,fy;
} a[100011];
int b[100011];
void add(int x,int y,long long w,long long fy)
{
 a[tot].to=y,a[tot].w=w,a[tot].net=head[x],a[tot].fy=fy,head[x]=tot++;
 return;
}
bool spfa()
{
 memset(dep,0x3f,sizeof(dep));
 memset(book,0,sizeof(book));
 queue<int> u;
 u.push(s);
 book[s]=1,dep[s]=0,in[s]=0x7fffffff;
 while (u.size())
 {
  y=u.front();
  u.pop();
  book[y]=0;
  for (int j=head[y];j;j=a[j].net)
  {
   if (a[j].w!=0&&dep[y]+a[j].fy<dep[a[j].to])
   {
    dep[a[j].to]=dep[y]+a[j].fy;//记录单价和 
    in[a[j].to]=min(in[y],a[j].w);
    b[a[j].to]=j;
    if (!book[a[j].to]) book[a[j].to]=1,u.push(a[j].to);
   }
  }
 }
 return dep[t]!=1061109567;
}
void dfs()
{
 x=t;
 ans+=in[t];
 ans2+=dep[t]*in[t];
 while (x!=s)
 {
  a[b[x]].w-=in[t],a[b[x]^1].w+=in[t],x=a[b[x]^1].to; 
 }//清空增广路 
 return;
}
int main()
{
 scanf("%d",&n);
 s=n+1,t=n+2;
 for (int i=1;i<=n;i++)
 {
  scanf("%d",&dep[i]);
  ans+=dep[i];
  add(x,y,w,1);
  add(y,x,0,-1);
 }
 ans/=n;
 for (int i=1;i<=n;i++)
 {
  add(i,(i==n?1:i+1),1e30,1);
  add((i==n?1:i+1),i,0,-1);
  add((i==n?1:i+1),i,1e30,1);
  add(i,(i==n?1:i+1),0,-1);
  if (dep[i]>ans)
  {
   add(s,i,dep[i]-ans,0);
   add(i,s,0,0);
  }
  if (dep[i]<ans)
  {
   add(i,t,ans-dep[i],0);
   add(t,i,0,0);
  }
 }
 ans=0;
 while (spfa())
 {
  dfs();
 }
 cout<<ans2;
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值