题目描述
G 公司有 n 个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等。如何用最少搬运量可以使 n 个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。
问题分析
目标状态为所有仓库货物数量相等,每个仓库目前的货物数量减平衡数量 a[i]-s > 0 时,此仓库将向外运输货物,否则接收货物,所以我们的目标即为将所有货物从供应货物处转移到需求货物处,并使得转移花费最小.
所以从源点向每一个供应货物的点建边,容量为当前点可供应货物数;从每一个需求货物点向汇点建边,容量为需求货物数.每两个相邻的点之间都彼此有边,容量INF,单位花费为1.在当前网络上跑最小费用最大流即为答案.
代码
//最小费用最大流 复杂度O(N*M*F) //有向图 #include <bits/stdc++.h> using namespace std; #define N 105 #define M 20005 int s,t; int dis[N],vis[N],pre[N],head[N]; int cnt; struct Edge{ int to,next,w,c;//w容量,c花费 }edge[M<<1]; int INF = 0x3f3f3f3f; void ad(int u,int v,int w,int c){ edge[cnt].to = v,edge[cnt].next = head[u],edge[cnt].w = w,edge[cnt].c = c,head[u] = cnt++; edge[cnt].to = u,edge[cnt].next = head[v],edge[cnt].w = 0,edge[cnt].c = -c,head[v] = cnt++; } int spfa(){ memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); //memset(pre,-1,sizeof(pre)); pre[s] = pre[t] = -1; queue<int> Q; int u,v; while(!Q.empty()) Q.pop(); Q.push(s); vis[s] = 1;dis[s] = 0; while(!Q.empty()){ u = Q.front(); vis[u] = 0; Q.pop(); for(int i = head[u];i != -1;i = edge[i].next){ v = edge[i].to; if(!edge[i].w) continue; if(dis[v] > dis[u] + edge[i].c){ dis[v] = dis[u] + edge[i].c; pre[v] = i^1; if(!vis[v]) Q.push(v); vis[v] = 1; } } } if(pre[t] == -1) return 0; return dis[t]; } int flow(){ int f = INF; for(int i = pre[t];i != -1;i = pre[edge[i].to]){ f = min(f,edge[i^1].w); } for(int i = pre[t];i != -1;i = pre[edge[i].to]){ edge[i].w += f; edge[i^1].w -= f; } return f; } int EK(){ int cost,sum = 0; while((cost = spfa())) sum += cost * flow(); return sum; } int num[N]; void getMap(){ int n; int sum = 0; scanf("%d",&n); for(int i = 1;i <= n;++i){ scanf("%d",&num[i]); sum += num[i]; } for(int i = 1;i <= n;++i){ num[i] = num[i] - sum/n; if(num[i] > 0) ad(0,i,num[i],0); else ad(i,n+1,-num[i],0); } ad(n,1,INF,1);ad(1,n,INF,1); for(int i = 1;i < n;++i){ ad(i,i+1,INF,1); ad(i+1,i,INF,1); } s = 0,t = n+1; //建图 //初始化s,t } int main() { memset(head,-1,sizeof(head)); cnt = 0; getMap(); printf("%d\n",EK()); return 0; }
强无敌的另解贪心:
先考虑一个更简单的负载平衡问题:
n个人站成一行,每个人手中有a[i]张纸牌,每次传递纸牌必须是一个人将一张纸牌交给与之相邻的另一个人,问最少传递多少次能使所有人手中的纸牌数相同.
问题分析:
首先传递纸牌不能交叉传递,所以每一个人要么从外界接收纸牌,要么向外界输送。
平衡状态时,每个人手中都有s张纸牌。纸牌一定是从一个多余纸牌的区域传递向缺少纸牌的区域。假如前i个人共有sum[i]张纸牌,他们在平衡状态时应有s*i张纸牌,那么他们缺少(多余)的纸牌一定来源于第i+1个人,第i+1个人会向第i个人运送| s*i-sum[i] |张纸牌。至于第i+1个人的纸牌,是从更后面传递过来的。所以最终传递纸牌的花费即为∑|sum[i]-s*i|。复杂度O(n)
原问题:
站成一圈时,我们可以枚举两个彼此不交换卡牌的人,然后把环扯成一条链,问题又转化为简化问题了。
复杂度O(n*n)