p.s:
① 此随笔着重讲的是建模思路,可能不会贴上代码。
② 以下出现的S都代表源点,T都代表汇点。
1、飞行员配对问题
Solution:
二分图匹配模板。可以用匈牙利算法,也可用最大流。
网络流的建模:
由S向左部图连容量为1的边,右部图向T连容量为1的边,左右部图间根据给出的条件,对应连上一条容量为1的边。
2、分配问题
二分图带权匹配模板。可以用KM算法,也可以用费用流。
费用流的建模:
在二分图最大匹配建图的基础上,给左右部图间的连边赋上1的费用,其他边费用为0。
3、试题库问题
二分图多重匹配模板。
网络流的建模:
在二分图最大匹配建图的基础上,只需要把T连向左部图(左部图为题中试题类型)的边容量改为需要的试题量。
4、圆桌问题
实际上可以说应该说是多重匹配问题。
这个合适上一个题的区别就在于,上一题只有左部图可以连多条边,而此题左右部图都可以。
所以建模的话,只需在上述基础上,右部图再往T连对应容量的边即可。
5、负载平衡问题
这个题其实费用流才是真暴力。。。数学做法只要O(nlogn)。
但是,既然是24题当然得用网络流知识做嘛。。
这个建模没那么好想:
首先,我们需要注意到,一个点它只可以与前面的点或后面的点进行交换,且要么补给别人,要么接受别人补给。
对于这种只有出入两种情况的,一般启发着往拆点二分图上靠。
那么,左部图的点向右部图的点连的边表示左部给右部补给。
然后,可以把所有的点需要或多余的给计算出来。
对于一个点,如果它有多余,那么从它对应的左节点 向 相邻的两节点的右节点 连容量为inf,费用为1的边。
然后,S连向这些节点的左节点,容量为多余的量,费用为0。
对于一个点,如果它需要别人补给,那么向T连一条容量为需要的量的边。
但是还有一个很重要的点!!
那就是,对于有多余的点,还需向相邻节点的左节点连一条连容量为inf,费用为1的边!!!
这就相当于:这个点并不直接向两侧的点供应,而是把他们当做中转站而已。。
Code↓:
#include<bits/stdc++.h>
#define RG register
#define IL inline
using namespace std;
const int N=110;
const int inf=0x3f3f3f3f;
queue<int> q;
int n,S,T,cost,s[N],a[N],tot=1,pre[N<<2],vis[N<<2],dis[N<<2],head[N<<2],Minf[N<<2];
struct EDGE{int next,to,v,w;}e[N*N];
IL void make(int a,int b,int c,int d) {
e[++tot]=(EDGE){head[a],b,c,d},head[a]=tot;
e[++tot]=(EDGE){head[b],a,0,-d},head[b]=tot;
}
IL int spfa() {
RG int i,x,y;
while(!q.empty()) q.pop();
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
q.push(S),vis[S]=1,dis[S]=0,Minf[S]=inf;
while(!q.empty()) {
x=q.front(),vis[x]=0,q.pop();
for(i=head[x];i;i=e[i].next) {
if(e[i].v&&dis[y=e[i].to]>dis[x]+e[i].w) {
dis[y]=dis[x]+e[i].w;
pre[y]=i,Minf[y]=min(Minf[x],e[i].v);
if(!vis[y]) vis[y]=1,q.push(y);
}
}
}
return dis[T]!=inf;
}
IL void update() {
RG int i,now=T;
while(now!=S) {
i=pre[now];
e[i].v-=Minf[T],e[i^1].v+=Minf[T];
now=e[i^1].to;
}
cost+=Minf[T]*dis[T];
}
int main()
{
RG int i,ave,sum=0;
scanf("%d",&n);
for(i=1;i<=n;++i) scanf("%d",&s[i]),sum+=s[i];
for(i=1,ave=sum/n;i<=n;++i) s[i]-=ave;
S=1,T=2*n+2;
for(i=1;i<=n;++i) {
if(s[i]>0) make(S,i+1,s[i],0);
else make(i+n+1,T,-s[i],0);
}
for(i=1;i<=n;++i) {
if(i!=1) make(i+1,i,inf,1),make(i+1,i+n,inf,1);
if(i!=n) make(i+1,i+2,inf,1),make(i+1,i+2+n,inf,1);
}
make(2,n+1,inf,1),make(2,n*2+1,inf,1);
make(n+1,2,inf,1),make(n+1,n+2,inf,1);
while(spfa()) update();
printf("%d\n",cost);
return 0;
}