1.飞行员配对方案问题
二分图匹配模板题
2.负载平衡问题
题意:
公司有 n个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等。如何用最少搬运量可以使 n个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。输出最少搬运量。
思路:
求出平均数,显然最后所有仓库的货物数量都是平均数。如果点u的货物数比平均数大w,则源点到u有一条容量为w;费用为0的边,如果点u的货物数比平均数小w,则u到汇点有一条容量为w,费用为0的边。相邻两点之间有容量无穷大,费用为1的边,跑费用流即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxm=1e3+5; const int maxn=105; const int INF=1e9; struct edge { int v,next,w,f; } E[maxm]; int tot=1,head[maxn];//tot必须设为1才能使反向边用^得到 void addedge(int u,int v,int w,int f) { E[++tot].v=v; E[tot].w=w; E[tot].f=f; E[tot].next=head[u]; head[u]=tot; } int n,m,s,t; int dis[maxn],vis[maxn]; int q[maxn]; bool spfa(){ fill(dis,dis+n+1,INF); fill(vis,vis+n+1,0); int l=0,r=1; q[1]=t; dis[t]=0;//反向跑最短路 while(l!=r){ int u=q[l=l==n?0:l+1]; vis[u]=0; for(int i=head[u];i;i=E[i].next){ int v=E[i].v; int f=-E[i].f;//当前遍历的是反向的边,费用是负的 if(E[i^1].w&&dis[v]>dis[u]+f){//反向的边才是流的时候走的边,容量要>0 dis[v]=dis[u]+f; if(!vis[v]){ if(dis[v]>dis[l+1])q[r=r==n?0:r+1]=v; else q[l]=v,l=l==0?n:l-1;//SLF优化 vis[v]=1; } } } } return dis[s]<INF; } int maxf,cost; int dfs(int u,int flow){ if(u==t){ vis[t]=1; return flow; } int used=0; vis[u]=1; for(int i=head[u];i;i=E[i].next){ int v=E[i].v,w=E[i].w,f=E[i].f; if(!vis[v]&&w&&dis[u]==dis[v]+f){//满足增广条件 int temp=dfs(v,min(w,flow-used)); if(temp){ cost+=temp*f; E[i].w-=temp; E[i^1].w+=temp; used+=temp; } if(used==flow)break; } } return used; } void mincmaxf() { maxf=cost=0; while(spfa()) { vis[t]=1; while(vis[t]) { memset(vis,0,sizeof vis); maxf+=dfs(s,1e9);//一直増广直到走不到为止 } } } void add(int u,int v,int w,int f){ addedge(u,v,w,f); addedge(v,u,0,-f); } int a[105]; int main() { int nn; cin>>nn; n=nn+2;s=nn+1;t=nn+2; int ave=0; for(int i=1;i<=nn;i++){ scanf("%d",&a[i]); ave+=a[i]; } ave/=nn; for(int i=1;i<=nn;i++){ if(a[i]>ave){ add(s,i,a[i]-ave,0); } if(a[i]<ave){ add(i,t,ave-a[i],0); } } for(int i=1;i<=nn-1;i++){ add(i,i+1,1e9,1); add(i+1,i,1e9,1); } add(1,nn,1e9,1); add(nn,1,1e9,1); mincmaxf(); printf("%d\n",cost); }
3.运输问题
题意:
公司有 m个仓库和 n个零售商店。第 i个仓库有 a个单位的货物;第 j个零售商店需要 b个单位的货物。
货物供需平衡,即\(\sum\limits_{i=1}^{m}a_i=\sum\limits_{j=1}^{n}b_j\)。
从第i个仓库运送每单位货物到第 j 个零售商店的费用为Cij 。
问使所有货物都运走,总费用最大为多少,最小为多少?
思路:
求最小是最小费用最大流模板,求最大只要把边全部变成相反数的再跑一遍即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxm=1e5+5; const int maxn=205; const int INF=1e9; struct edge { int v,next,w,f; } E[maxm]; int tot=1,head[maxn];//tot必须设为1才能使反向边用^得到 void addedge(int u,int v,int w,int f) { E[++tot].v=v; E[tot].w=w; E[tot].f=f; E[tot].next=head[u]; head[u]=tot; } int n,m,s,t; int dis[maxn],vis[maxn]; int q[maxn]; bool spfa(){ fill(dis,dis+n+1,INF); fill(vis,vis+n+1,0); int l=0,r=1; q[1]=t; dis[t]=0;//反向跑最短路 while(l!=r){ int u=q[l=l==n?0:l+1]; vis[u]=0; for(int i=head[u];i;i=E[i].next){ int v=E[i].v; int f=-E[i].f;//当前遍历的是反向的边,费用是负的 if(E[i^1].w&&dis[v]>dis[u]+f){//反向的边才是流的时候走的边,容量要>0 dis[v]=dis[u]+f; if(!vis[v]){ if(dis[v]>dis[l+1])q[r=r==n?0:r+1]=v; else q[l]=v,l=l==0?n:l-1;//SLF优化 vis[v]=1; } } } } return dis[s]<INF; } int maxf,cost; int dfs(int u,int flow){ if(u==t){ vis[t]=1; return flow; } int used=0; vis[u]=1; for(int i=head[u];i;i=E[i].next){ int v=E[i].v,w=E[i].w,f=E[i].f; if(!vis[v]&&w&&dis[u]==dis[v]+f){//满足增广条件 int temp=dfs(v,min(w,flow-used)); if(temp){ cost+=temp*f; E[i].w-=temp; E[i^1].w+=temp; used+=temp; } if(used==flow)break; } } return used; } void mincmaxf() { maxf=cost=0; while(spfa()) { vis[t]=1; while(vis[t]) { memset(vis,0,sizeof vis); maxf+=dfs(s,1e9);//一直増广直到走不到为止 } } } void add(int u,int v,int w,int f){ addedge(u,v,w,f); addedge(v,u,0,-f); } int a[105],b[105],c[105][105]; int main() { int nn,mm; cin>>mm>>nn; n=nn+mm+2; s=nn+mm+1; t=nn+mm+2; for(int i=1;i<=mm;i++){ scanf("%d",&a[i]); add(s,i,a[i],0); } for(int i=1;i<=nn;i++){ scanf("%d",&b[i]); add(mm+i,t,b[i],0); } for(int i=1;i<=mm;i++){ for(int j=1;j<=nn;j++){ scanf("%d",&c[i][j]); add(i,mm+j,1e9,c[i][j]); } } mincmaxf(); printf("%d\n",cost); //重新建立费用为相反数的模型 tot=1; memset(head,0,sizeof(head)); for(int i=1;i<=mm;i++){ add(s,i,a[i],0); } for(int i=1;i<=nn;i++){ add(mm+i,t,b[i],0); } for(int i=1;i<=mm;i++){ for(int j=1;j<=nn;j++){ add(i,mm+j,1e9,-c[i][j]); } } mincmaxf(); printf("%d\n",-cost); }