难怪评蓝题,实在是太裸了。
源点向每个点连边,容量为这个点的现有货物数。
每个点向汇点连边,容量为要求即sum/n。
每个点向其相邻两点连边,容量无限,费用为1。
然后跑一遍源点到汇点的费用流。
看代码:
#include<bits/stdc++.h> using namespace std; #define int long long #define inf 1e12 const int maxn=1e5+10; int n,a[maxn],sum,ans; int beg[maxn],nex[maxn],to[maxn],w[maxn],cost[maxn],e; queue<int>q; inline int read(){ int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();} return x*f; } inline void add(int x,int y,int z,int c){ nex[e]=beg[x];beg[x]=e;to[e]=y; w[e]=z;cost[e]=c;e++; } int dis[maxn],vis[maxn],las[maxn],ed[maxn],flow[maxn]; inline int spfa(){ memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[0]=0;vis[0]=1;flow[0]=inf; las[n+1]=-1;q.push(0); while(!q.empty()){ int x=q.front(); q.pop();vis[x]=0; for(int i=beg[x];~i;i=nex[i]){ int t=to[i]; if(w[i]&&dis[t]>dis[x]+cost[i]){ dis[t]=dis[x]+cost[i]; las[t]=x;ed[t]=i; flow[t]=min(flow[x],w[i]); if(!vis[t]){ vis[t]=1;q.push(t); } } } } return las[n+1]!=-1; } signed main(){ memset(beg,-1,sizeof(beg)); n=read(); for(int i=1;i<=n;i++){ a[i]=read(); sum+=a[i]; } for(int i=1;i<=n;i++){ add(0,i,a[i],0); add(i,0,0,0); add(i,n+1,sum/n,0); add(n+1,i,0,0); add(i,i%n+1,inf,1); add(i%n+1,i,0,-1); add(i%n+1,i,inf,1); add(i,i%n+1,0,-1); } while(spfa()){ ans+=dis[n+1]*flow[n+1]; int tmp=n+1; while(tmp){ w[ed[tmp]]-=flow[n+1]; w[ed[tmp]^1]+=flow[n+1]; tmp=las[tmp]; } } printf("%lld ",ans); return 0; }
深深地感到自己的弱小。