这道题,看上去就发现是一个最小费用最大流了,这里还是用的 spfa 写的
首先需要注意一点,每个十字路口只能走一次,那么还是运用我们的拆点方法,来限制入点到出点,
第二次接触带有最小费用的最大流。。。
下边这些话摘选自一个大犇的博客:https://blog.csdn.net/liusu201601/article/details/79443323
解题思路:
1、请结合最大流的模板对比学习,会更加容易搞到懂;
2、这题因为每条边的流量为 1,所以,是个良心的过渡型模板题,但是费用流和最大流在实现上有一个函数的区别:
1)最大流:是用 bfs 将点分层,然后 跑 dfs 推流量;
2)费用流:还是用 bfs 分层,但是(按最小费用)来分层,其实就是跑 spfa(spfa不懂的朋友,下次咱们再复习);
3)每次 spfa 的过程,用 a[x].f 记录来路;跑完一次完整的spfa之后,可以算出(当前这次流量)的最小费用;
4)根据 a[x].f 的记录,用循环往回跑,找出这次用过的边,做一下流量反向。
5)其实基础思维和最大流差不多,只是分层的机制改成了费用,并且将 dfs 函数 省掉(感觉会慢一点点,猜的)
3、构图也比较简单,因为每个点只能跑一次,直接拆点就可以了。
首先 我的 bfs 思路是设的父节点。。因为以前搞过一个模板。。
而且对于每个点 i+n 是出点 , i 是入点 ,cap[ a ] [ b ] 设为 1 cost [ a ] [ b ] = c
然后 spfa 跑一下,再根据父节点跑一边增广路,多跑几次 spfa 直到无路可走。
mxflow 就是 要跑几次, mxcost 就是我们最小距离(费用)
以下是 AC 代码
#include
#include
#include
#include
using namespace std;
const int maxn = 500;
const int maxm = 405;
const int INF = 0x3f3f3f3f;
int n,m;
int mxflow,mxcost;
int flow[maxn],vis[maxn];
int pre[maxn],dis[maxn];
int cost[maxm][maxm];
int cap[maxm][maxm];
int st,en;
int bfs()
{
queueq;
memset(pre,-1,sizeof(pre));
memset(dis,INF,sizeof(dis));
memset(flow,0,sizeof(flow));
q.push(st);
pre[st] = 0;
dis[st] = 0;
vis[st] = 1;
flow[st] = INF;
while(q.size())
{
int u = q.front();
q.pop();
vis[u] = 0;
for(int i=1;i<2*n;i++)
{
if(cap[u][i]>0 && dis[i] > dis[u] + cost[u][i])
{
dis[i] = dis[u] + cost[u][i];
flow[i] = min(cap[u][i], flow[u]);
pre[i] = u;
if(!vis[i])
{
vis[i] = 1;
q.push(i);
}
}
}
}
if(pre[en] == -1)
return -1;
else
return flow[en];
}
void maxflow()
{
int rec;
while(true)
{
rec = bfs();
if(rec == -1)
return;
int t = en;
while(t!=st)
{
cap[pre[t]][t] -= rec;
cap[t][pre[t]] += rec;
t = pre[t];
}
mxflow += rec;
mxcost += rec*dis[en];
}
}
int main()
{
scanf("%d%d",&n,&m);
st = 1, en = n;
for(int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
int te;
if(a==1) te = 1;
else te = a + n;
cap[te][b] = 1;
cost[te][b] = c;
cost[b][te] = -c;
}
for(int i=2;i