[网络流24题]
https://www.luogu.org/problemnew/show/P4016
题目描述
GGG 公司有 nnn 个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等。如何用最少搬运量可以使 nnn 个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。
输入输出格式
输入格式:
文件的第 1 行中有 1 个正整数 n,表示有 n 个仓库。
第 2 行中有 n 个正整数,表示 n 个仓库的库存量。
输出格式:
输出最少搬运量。
输入输出样例
输入样例#1:
5 17 9 14 16 4
输出样例#1:
11
说明
1<=n<=100
题解:1)建图,设置超级源点0和超级汇点(n+1),源点向每个高于平均值的点连一条流量为差值,费用为0的边,并弄一条流量为0且费用为0的反向边(用于回退,也是求网络流的要点),每个低于平均值的点向汇点连一条流量为差值,费用为0的边,并弄一条流量为0且费用为0的反向边,然后相邻的两点连一条流量为inf,费用为1的边以及一条流量为0,费用为-1的反向边 2)直接求最小费用最大流即可
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<vector> 6 #include<queue> 7 using namespace std; 8 //#define io_test 9 #define debug(x) cout<<x<<"#######"<<dist[x]<<endl; 10 typedef long long ll; 11 ll a[105]; 12 const ll inf=0x3f3f3f3f; 13 struct edge{ 14 int x; 15 int y; 16 ll v; 17 ll c; 18 int nex; 19 }e[1005]; 20 ll flow,res; 21 int cnt,head[505],n; 22 void adde(int x1,int x2,ll v1,ll v2){ 23 e[cnt].c=v2; 24 e[cnt].v=v1; 25 e[cnt].x=x1; 26 e[cnt].y=x2; 27 e[cnt].nex=head[x1]; 28 head[x1]=cnt++; 29 } 30 int dist[505],prevv[505],preve[1005]; // 最短路的前驱节点 和 对应的边 31 inline void min_cost_flow(int s,int t,int f) 32 { 33 while(f > 0) 34 { 35 memset(dist,inf,sizeof dist); 36 dist[s] = 0; 37 bool update = true; 38 while(update) 39 { 40 update = false; 41 for(int i=0;i<=n+1;i++) 42 { 43 if(dist[i] == inf) continue; 44 for(int j=head[i];j!=-1; j=e[j].nex) 45 { 46 if(e[j].v > 0 && dist[e[j].y] > dist[i] + e[j].c) 47 { 48 dist[e[j].y] = dist[i] + e[j].c; 49 prevv[e[j].y] = i; 50 preve[e[j].y] = j; 51 update = true; 52 } 53 } 54 } 55 } 56 // 此时的 INF 表示再也没有 增广路径,于是就是最后的答案了! 57 if(dist[t] == inf) break; 58 ll d = f; 59 for(int v0 = t; v0 != s; v0 = prevv[v0]) 60 {d = min(d,e[preve[v0]].v);} 61 // d 就是 增广路的 流量! 62 f -= d; 63 flow += d; 64 res += d * dist[t]; 65 for(int v0=t;v0!=s;v0=prevv[v0]) 66 { 67 e[preve[v0]].v -= d; 68 e[preve[v0]^1].v += d; 69 } 70 } 71 } 72 int main() 73 { 74 #ifdef io_test 75 freopen("in.txt","r",stdin); 76 freopen("out.txt","w",stdout); 77 #endif // io_test 78 scanf("%d",&n); 79 ll sum=0; 80 memset(head,-1,sizeof(head)); 81 for(int i=1;i<=n;i++){scanf("%lld",&a[i]);sum+=a[i];} 82 for(int i=1;i<=n;i++){ 83 if(a[i]>sum/n){adde(0,i,a[i]-sum/n,0);adde(i,0,0,0);} 84 if(a[i]<sum/n){adde(i,n+1,-a[i]+sum/n,0);adde(n+1,i,0,0);} 85 if(i==n){ 86 adde(n,1,inf,1); 87 adde(1,n,0,-1); 88 adde(1,n,inf,1); 89 adde(n,1,0,-1); 90 } 91 else{ 92 adde(i,(i+1),inf,1); 93 adde(i+1,(i),0,-1); 94 adde((i+1),i,inf,1); 95 adde((i),i+1,0,-1); 96 } 97 } 98 min_cost_flow(0,n+1,inf); 99 printf("%lld\n",res); 100 return 0; 101 }