算法
处理最小费用最大流问题时,笔者最经常使用的算法是连续最短路算法。比较符合笔者很弱的代码能力,而且理解起来较为简单。
因为边的描述中多了权值这一元素,所以在建图的时候应该与最大流算法有些微小的差异。相同的地方是,两种问题都需要建反向边且反向边的流量都是0,对于边的权值,我们将反向边的权值设为正边的权值的相反数。
连续最短路算法的流程如下:
- hhh
- 用spfa找出图中所有可行路径(不包含流量为0的边的路径)具有最短距离的路径,如果有可行路径则进行第二步,否则跳至第3步;
- 对spfa中处理出的路径进行增广,并将这条路径的流量加入答案,将这条路径上边的权值和加入答案;
- 算法结束。
例题选讲
~
BZOJ1877 晨跑
题目大意
给出一个有向图,每个点能且只能经过一次,注明每一条边的权值。问在从起点到重点的路径尽可能多路径的权值和最短的情况下,最多的路径条数和最小的花费。
思路&题解
我们发现这道题里引入了权值这个量,所以是一个费用流相关问题。因为每一个点只能通过一次,所以我们采用和上一篇文章一样的方法:把每一个点拆成两个点,并在两个点中间连上一条流量为1权值为0的边,以出发点为源点,以终点为汇点,跑一遍最小费用最大流即可。
代码
/**************************************************************
Problem: 1877
User: CHN
Language: C++
Result: Accepted
Time:1200 ms
Memory:14280 kb
****************************************************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <iostream>
using namespace std;
#define pos1(k) (k+1)
#define pos2(k) (k+n+1)
const int maxn=int(1e5)+10;
const int INF=int(1e9)+7;
int n,m;
int S,T;
int ans_flow=0;
int ans_dis=0;
struct Edge {
int from,to;
int val,flow;
int next;
}eage[maxn*6];
int head[maxn];
int tot=-1;
void add(int x,int y,int f,int v) {
eage[++tot].from=x;
eage[tot].to=y;
eage[tot].val=v;
eage[tot].flow=f;
eage[tot].next=head[x];
head[x]=tot;
std::swap(x,y);
f=0;
v=-v;
eage[++tot].from=x;
eage[tot].to=y;
eage[tot].val=v;
eage[tot].flow=f;
eage[tot].next=head[x];
head[x]=tot;
}
void init() {
memset(head,-1,sizeof head);
tot=-1;
}
int dis[maxn];
bool used[maxn];
int pre[maxn];
queue <int> que;
void dfs();
bool spfa() {
while(que.size()) que.pop();
fill(dis,dis+maxn,INF);
memset(used,false,sizeof used);
dis[S]=0;
used[S]=true;
que.push(S);
while(que.size()) {
int u=que.front(); que.pop();
used[u]=false;
for(int i=head[u];~i;i=eage[i].next) if(eage[i].flow) {
int v=eage[i].to;
if(dis[v]<=dis[u]+eage[i].val) continue;
dis[v]=dis[u]+eage[i].val;
pre[v]=i;
if(!used[v]) {
used[v]=true;
que.push(v);
}
}
}
if(dis[T]>=INF) return false;
ans_dis+=dis[T];
return true;
}
void dfs() {
int now=T,p=pre[T];
int min_flow=INF;
for(;now!=S;) {
min_flow=std::min(min_flow,eage[p].flow);
now=eage[p^1].to;
p=pre[now];
}
now=T,p=pre[T];
for(;now!=S;) {
eage[p].flow-=min_flow;
eage[p^1].flow+=min_flow;
now=eage[p^1].to;
p=pre[now];
}
ans_flow+=min_flow;
return;
}
void Min_cost_Max_flow() {
while(spfa()) dfs();
}
int main() {
scanf("%d%d",&n,&m);
init();
S=pos1(1)-1;
T=pos2(n)+1;
add(S,pos1(1),INF,0);
add(pos2(n),T,INF,0);
add(pos1(1),pos2(1),INF,0);
add(pos1(n),pos2(n),INF,0);
for(int i=2;i<n;i++)
add(pos1(i),pos2(i),1,0);
for(int i=1;i<=m;i++) {
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(pos2(x),pos1(y),1,z);
}
Min_cost_Max_flow();
printf("%d %d\n",ans_flow,ans_dis);
return 0;
}