网络流/费用流题目总结[持续更新]
//看心情填坑...
飞行员配对方案问题 24题
裸二分图匹配,跑一边Dinic即可,原理类似网络流的Hopcroft-Karp算法在二分图的时间复杂度可以到达$O( sqrt(n)* m )$
实际上Dinic的原理和它类似,实现中也可以跑的很快
#include <bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define pp pair<int,int>
#define rep(ii,a,b) for(auto ii=a;ii<=b;ii++)
#define per(ii,a,b) for(auto ii=a;ii>=b;ii--)
#define show(x) cout<<#x<<"="<<x<<endl
#define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
#define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
using namespace std;
const int maxn=1e5+10;
const int maxm=1e6+10;
const int INF=0x3f3f3f3f;
int casn,n,m,k;
int to[maxm],cap[maxm],nex[maxm];
int head[maxn],S,T,nume,ans,dis[maxn];
int q[maxn];
void add(int a,int b,int c){
to[nume]=b,cap[nume]=c,nex[nume]=head[a];
head[a]=nume++;
to[nume]=a,cap[nume]=0,nex[nume]=head[b];
head[b]=nume++;
}
bool bfs(){
memset(dis,-1,sizeof dis);
int top=0,end=0;
q[end++]=S;
dis[S]=0;
while(top!=end){
int now=q[top++];top%=maxn;
for(int i=head[now];~i;i=nex[i])
if(dis[to[i]]==-1&&cap[i]){
dis[to[i]]=dis[now]+1;
q[end++]=to[i];end%=maxn;
}
}
return dis[T]!=-1;
}
int dfs(int now,int last){
if(now==T) return last;
int used=0,flow;
for(int i=head[now];~i;i=nex[i]){
if(cap[i]&&dis[to[i]]==dis[now]+1){
flow=last-used;
flow=dfs(to[i],min(flow,cap[i]));
used+=flow;
cap[i]-=flow;
cap[i^1]+=flow;
if(used==last) return last;
}
}
if(used==0) dis[now]=-1;
return used;
}
void dinic(){
ans=0;
while(bfs()) ans+=dfs(S,0x3f3f3f3f);
}
int main(){
//#define test
#ifdef test
freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif
cin>>m>>n;
S=0;T=n+1;
memset(head,-1,sizeof head);
int a,b;
while(cin>>a>>b,~a&&~b) add(a,b,1);
for(int i=1;i<=m;i++) add(S,i,1);
for(int i=m+1;i<=n;i++) add(i,T,1);
dinic();
if(ans) {
cout<<ans<<endl;
for(int i=0;i<nume;i+=2) if(to[i]!=S&&to[i^1]!=S&&to[i]!=T&&to[i^1]!=T&&cap[i^1]!=0){
printf("%d %d\n",to[i^1],to[i]);
}
}
else cout<<"No Solution!"<<endl;
#ifdef test
fclose(stdin);fclose(stdout);system("out.txt");
#endif
return 0;
}
负载平衡问题 24题
G 公司有 n个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等。如何用最少搬运量可以使 n个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。
求最小搬运量
最小费用最大流,首先按照题意所说,建立一个环形的路线,费用为1,容量$INF$
其次,对于大于平均值的,需要向其他仓库输出,换句话说都是些小源点,那就从源点向他链接一条边,容量为多余量,费用为0,
然后,相对应的,对于小于平均值的,需要接受,换句话说都是小汇点,链接到汇点的边,容量为需要的量,费用也是0,
最后跑一边最小费用最大流,由于建图方式,超级源点流出的肯定等于超级汇点收到的,
且流出的仓库肯定不会多输出,流入的仓库也不会多接受,符合要求.
#include <bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define pp pair<int,int>
#define rep(ii,a,b) for(auto ii=a;ii<=b;ii++)
#define per(ii,a,b) for(auto ii=a;ii>=b;ii--)
#define show(x) cout<<#x<<"="<<x<<endl
#define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
#define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
using namespace std;
const int maxn=10000+10;
const int maxm=10000+10;
const int INF=0x3f3f3f3f;
int casn,n,m,k;
struct node {
int pre,to,cap,cost,next;
}e[maxm];
int head[maxn],nume,inq[maxn],sum,ans;
int que[maxn],pre[maxn],dis[maxn];
int num[maxn],S,T;
inline void addedge(int a,int b,int c,int d){
e[++nume]={a,b,c,d,head[a]};
head[a]=nume;
}
inline void add(int a,int b,int c,int d){
addedge(a,b,c,d);addedge(b,a,0,-d);
}
bool spfa(){
for(int i=0;i<=T;i++)dis[i]=INF;
dis[S]=que[0]=S;
int top=0,end=1;
while(top!=end){
int now=que[top++];top%=maxn;
for(int i=head[now];i;i=e[i].next){
if(e[i].cap&&dis[e[i].to]>dis[now]+e[i].cost){
pre[e[i].to]=i;
dis[e[i].to]=dis[now]+e[i].cost;
if(!inq[e[i].to]){
inq[e[i].to]=true;
que[end++]=e[i].to;end%=maxn;
}
}
}
inq[now]=false;
}
return dis[T]!=INF;
}
void dfs(){
int flow=INF;
for(int i=pre[T];i;i=pre[e[i].pre]) flow=min(flow,e[i].cap);
for(int i=pre[T];i;i=pre[e[i].pre]) {
e[i].cap-=flow;
e[i^1].cap+=flow;
ans+=e[i].cost*flow;
}
}
void mcf(){
ans=0;
while(spfa()) dfs();
}
int main(){
//#define test
#ifdef test
freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif
IO;
cin>>n;
nume=1;
S=0,T=3*n;
for(int i=1;i<=n;i++) cin>>num[i],sum+=num[i];
sum/=n;
for(int i=1;i<=n;i++) num[i]-=sum;
for(int i=1;i<=n;i++){
if(num[i]>0) add(S,i,num[i],0);
else add(i+n,T,-num[i],0);
}
for(int i=1;i<=n;i++){
if(i!=1){
add(i,i-1,INF,1);
add(i,i-1+n,INF,1);
}
if(i!=n){
add(i,i+1,INF,1);
add(i,i+1+n,INF,1);
}
}
add(n,1,INF,1);
add(n,1+n,INF,1);
add(1,n,INF,1);
add(1,n<<1,INF,1);
mcf();
cout<<ans<<endl;
#ifdef test
fclose(stdin);fclose(stdout);system("out.txt");
#endif
return 0;
}
两个人在一个棋盘上互相交换纸条,从左上到右下,第一个人的纸条只能向右或向下,反之亦然,
每一个点只能经过一次,并且有一个权值$G(i,j)$,要求两个纸条最终经过的所有点权值和最大
最大费用最大流....但是这个题标解应该是DP吧
两个人互相传,实际上等于一个人同时传了2个到下面,于是简化问题,
一开始朴素的建了图,结果错了2个CASE,
方式就是超级源向$(1,1)$连接一条费用0,容量2的边,超级汇点一个道理,然后就用容量1.费用为$G(i,j)$来模拟..最后发现会出现可能会出现一个点通过大小为2的流这种情况,题目实际上是限制每个点的最大流量...
所以..拆点大法好,每个点拆为2个点,一个入,一个出,从入到出连接2个边,一个费用为$G(i,j)$容量为1,一个费用为0,容量为1,这样即使有一个点通过大小为2的流量,也不会导致流量重复计算了
#include <bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define rep(ii,a,b) for(auto ii=a;ii<=b;ii++)
using namespace std;
const int maxn=1e5+10;
const int maxm=1e6+10;
const int INF=0x3f3f3f3f;
int casn,n,m,k;
int g[123][123];
struct node {
int pre,to,cap,cost,next;
}e[maxm];
int head[maxn],nume,inq[maxn],sum;
int que[maxn],pre[maxn],dis[maxn];
int S,T,ans;
inline void addx(int a,int b,int c,int d){
e[++nume]={a,b,c,d,head[a]};
head[a]=nume;
}
inline void add(int a,int b,int c,int d){
addx(a,b,c,d);addx(b,a,0,-d);
}
bool spfa(){
rep(i,0,T) dis[i]=-INF;
int top=0,end=1;
dis[S]=que[0]=0;
while(top!=end){
int now=que[top++];top%=maxn;
for(int i=head[now];i;i=e[i].next){
if(e[i].cap&&dis[e[i].to]<dis[now]+e[i].cost){
pre[e[i].to]=i;
dis[e[i].to]=dis[now]+e[i].cost;
if(!inq[e[i].to]){
inq[e[i].to]=true;
que[end++]=e[i].to;end%=maxn;
}
}
}
inq[now]=false;
}
return dis[T]!=-INF;
}
void dfs(){
int d=INF;
for(int i=pre[T];i;i=pre[e[i].pre]) d=min(d,e[i].cap);
for(int i=pre[T];i;i=pre[e[i].pre]) {
e[i].cap-=d;
e[i^1].cap+=d;
ans+=e[i].cost*d;
}
}
int main(){
//#define test
#ifdef test
freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif
IO;
cin>>n>>m;
rep(i,1,n){
rep(j,1,m){
cin>>g[i][j];
}
}
S=0,T=3*n*m;
int gap=n*m+2;
nume=1;
add(S,1,2,0);
add(m*n,T,2,0);
rep(i,1,n){
rep(j,1,m){
int s=(i-1)*m+j;
if(j<m||i<n){
if(i==1&&j==1) add(s,s+gap,2,0);
else add(s,s+gap,1,g[i][j]);
if(j<m) add(s+gap,s+1,1,0);
if(i<n) add(s+gap,s+m,1,0);
}
}
}
ans=0;
while(spfa()) {
dfs();
}
cout<<abs(ans)<<endl;
#ifdef test
fclose(stdin);fclose(stdout);system("out.txt");
#endif
return 0;
}

浙公网安备 33010602011771号