康复计划之 最小费用最大流
以前写的是直接在网上拿了一个模板,直接套了A……
这次好好在纸上模拟了一下,理解了一下。
最小费用最大流的增广路算法简单说就是不断求最短路(因为残量网络和反向边的存在),记录最短路,再进行流量处理,直到最后不存在通路,则停止。
下面是一个 入门题
题意:
给你一张图,要你从1 -> n 再从 n -> 1 不能走重复的路径。问你最短方案。
思路:
其实就是找出最短和次短的 1 -> n 的路径,当然路径不允许任何重叠。
单纯的两次spfa是不可行的。因为第一次最短路很可能会把通路断掉。
AC Code
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int inf = 99999999;
const int maxn = 1005;
const int maxe = 10005 * 4;
struct MCMF {
private:
int st, en, idx;
int head[maxn], dis[maxe], preID[maxe];
bool vis[maxe];
queue<int> que;
struct node {
int to, next, pre;
int flow, cost;
node() {}
node(int _to, int _next, int _pre, int _flow, int _cost)
: to(_to)
, next(_next)
, pre(_pre)
, flow(_flow)
, cost(_cost)
{
}
} edges[maxe];
bool spfa()
{
memset(vis, false, sizeof(vis));
for (int i = st; i <= en; i++)
dis[i] = inf;
vis[st] = true;
dis[st] = 0;
que.push(st);
int u;
while (!que.empty()) {
u = que.front();
que.pop();
for (int id = head[u]; ~id; id = edges[id].next) {
int v = edges[id].to;
if ((edges[id].flow > 0) && (dis[u] + edges[id].cost < dis[v])) {
dis[v] = dis[u] + edges[id].cost;
preID[v] = id;
if (!vis[v]) {
vis[v] = true;
que.push(v);
}
}
}
vis[u] = false;
}
return dis[en] != inf;
}
public:
void addEdge(int u, int v, int f, int c)
{
edges[idx] = node(v, head[u], u, f, c);
head[u] = idx++;
edges[idx] = node(u, head[v], v, 0, -c);
head[v] = idx++;
}
void init(int s, int e)
{
st = s, en = e;
memset(head, -1, sizeof head);
idx = 0;
}
int minCost()
{
int ans = 0;
while (spfa()) {
ans += dis[en];
for (int id = preID[en]; id != st; id = preID[edges[id].pre]) {
edges[id].flow--;
edges[id ^ 1].flow++;
}
}
return ans;
}
} mcmf;
int main()
{
int n, m;
scanf("%d%d", &n, &m);
mcmf.init(0, n + 1);
mcmf.addEdge(0, 1, 2, 0);
mcmf.addEdge(n, n + 1, 2, 0);
for (int i = 1; i <= m; i++) {
int u, v, c;
scanf("%d%d%d", &u, &v, &c);
mcmf.addEdge(u, v, 1, c);
mcmf.addEdge(v, u, 1, c);
}
printf("%d\n", mcmf.minCost());
return 0;
}