费用流:在网络流的边上加上一个费用值,在保证最大流的基础上,使这个费用最小。
实现方法:把dinic的bfs改成用费用跑spfa,dfs过程改成单一路径修改。
模板代码如下(变量解释在代码后):
const int lar=0x1f1f1f1f;
bool spfa()
{
queue<int> V;
memset(f,0x1f,sizeof(f));
memset(road,0,sizeof(road));
V.push(s);f[s]=0;b[s]=true;
while(!V.empty())
{
int c=V.front();
for(int t=fir[c];t;t=nes[t])
{
if(!q[t] || f[c]+w[t]>=f[v[t]]) continue;
f[v[t]]=f[c]+w[t]; road[v[t]]=t;
if(b[v[t]]) continue;
V.push(v[t]);b[v[t]]=true;
}
b[c]=false;
V.pop();
}
return f[e]!=lar;
}
void fly()
{
ans=0,cs=0;
while(spfa())
{
c=lar;
for(int t=e;t!=s;t=bak[road[t]]) c=min(q[road[t]],c);
for(int t=e;t!=s;t=bak[road[t]])
{
cs+=w[road[t]]*c;
q[road[t]]-=c;
q[road[t]^1]+=c;
}
ans+=c;
}
}
变量解释:
1、bak[i]代表编号为i的边的起点;
2、road[i]代表i节点为终点的边;
3、fly函数里的c代表最大可行流;
4、ans代表最大流,cs代表最小费用。
【SDOI2009】 bzoj1877 晨跑
题目大意:
给定n个点m条边带权图,要求找到最多的路径条数且使总费用尽量少,路径与路径之间不能相交。
题目分析:
把每个点拆成两个点,中间连一条流量为1的边,加上费用跑最大流。
代码如下:
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<queue>
#define N 44444
using namespace std;
const int lar=0x1f1f1f1f;
int n,m,x,y,z,ans,cs;
int fir[N],nes[N],v[N],q[N],w[N],top=1,bak[N],f[N],road[N];
bool b[N];
void edge(int x,int y,int z,int p)
{
top++;
v[top]=y;q[top]=z;w[top]=p;bak[top]=x;
nes[top]=fir[x];fir[x]=top;
}
bool spfa()
{
queue<int> V;
memset(f,0x1f,sizeof(f));
memset(road,0,sizeof(road));
V.push(1);f[1]=0;b[1]=true;
while(!V.empty())
{
int c=V.front();
for(int t=fir[c];t;t=nes[t])
{
if(!q[t] || f[c]+w[t]>=f[v[t]]) continue;
f[v[t]]=f[c]+w[t];road[v[t]]=t;
if(b[v[t]]) continue;
V.push(v[t]);b[v[t]]=true;
}
b[c]=false;
V.pop();
}
return f[2*n]!=lar;
}
void fly()
{
while(spfa())
{
int c=lar;
for(int t=2*n;t!=1;t=bak[road[t]]) c=min(c,q[road[t]]);
for(int t=n;t!=1;t=bak[road[t]])
{
cs+=w[road[t]]*c;
q[road[t]]-=c;
q[road[t]^1]+=c;
}
ans+=c;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=2;i<n;i++)
{
edge(i,i+n,1,0);
edge(i+n,i,0,0);
}
edge(1,1+n,lar,0);edge(1+n,1,0,0);
edge(n,2*n,lar,0);edge(2*n,n,0,0);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
edge(x+n,y,1,z);
edge(y,x+n,0,-z);
}
fly();
printf("%d %d\n",ans,cs);
return 0;
}