因为题目中限制了点只能用一次,所以我们利用拆点法来保证每个点只能用一次(最小费用最大流的算法会保证每条边用一次)
下面我们来看一下拆点法是怎么工作的,这是建图的代码
for(int i = 2; i < n; i++)
ans.AddEdge(i, i+n, 1, 0);
ans.AddEdge(1, 1+n, 2, 0);
ans.AddEdge(n, n+n, 2, 0);
for(int i = 0; i < m; i++) {
int u, v, s;
scanf("%d%d%d", &u,&v,&s); ans.AddEdge(u+n, v, 1, s);
}
1是源点,n是汇点,源点和汇点到其拆分出来的点的弧的容量为2,其他弧容量为1,下标为i的点被拆分成了i和i+n两个点,注意红线部分,为何要u+n呢,这就是拆点法的工作原理了,看图
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <iostream>
#include <cstring>
#include <vector>
#define inf 0x3f3f3f3f
#define maxn 3000
using namespace std;
struct Edge{
int from, to, cost, flow, cap;
Edge(int u, int v,int c, int f, int w):from(u), to(v), cap(c), flow(f), cost(w){}
};
struct MCMF{
int n, m, s, t;
vector<Edge> edge;
vector<int> G[maxn];
int inq[maxn];
int d[maxn];
int p[maxn];
int a[maxn];
void init(int n){
this->n = n;
for(int i = 1; i <= n; i++)
G[i].clear();
edge.clear();
}
void AddEdge(int from, int to, int cap, int cost){
edge.push_back(Edge(from, to, cap, 0, cost));
edge.push_back(Edge(to, from, 0, 0, -cost));
m = edge.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BellmanFord(int s, int t, int& flow, int& cost){
for(int i = 1; i <= maxn; i++) d[i] = inf;
memset(inq, 0, sizeof(inq));
d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = inf;
queue<int> que;
que.push(s);
while(!que.empty())
{
int u = que.front(); que.pop();
inq[u] = 0;
for(int i = 0; i < G[u].size(); i++)
{
Edge& e = edge[G[u][i]];
if(e.cap > e.flow && d[e.to] > d[u]+e.cost)
{
d[e.to] = d[u]+e.cost;
p[e.to] = G[u][i];
a[e.to] = min(a[u], e.cap-e.flow);
if(!inq[e.to])
{
que.push(e.to);
inq[e.to] = 1;
}
}
}
}
if(d[t] == inf) return false;
flow += a[t];
cost += d[t]*a[t];
for(int u = t; u != s; u = edge[p[u]].from)
{
edge[p[u]].flow += a[t];
edge[p[u]^1].flow -= a[t];
}
return true;
}
int mcmf(int s, int t)
{
int flow = 0;
int cost = 0;
while(BellmanFord(s, t, flow, cost));
return cost;
}
}ans;
int main()
{
int n, m;
while(scanf("%d%d", &n, &m)==2)
{
ans.init(2*n+7);
for(int i = 2; i < n; i++)
ans.AddEdge(i, i+n, 1, 0);
ans.AddEdge(1, 1+n, 2, 0);
ans.AddEdge(n, n+n, 2, 0);
for(int i = 0; i < m; i++)
{
int u, v, s;
scanf("%d%d%d", &u,&v,&s);
ans.AddEdge(u+n, v, 1, s);
}
int f = ans.mcmf(1, 2*n);
printf("%d\n",f);
}
return 0;
}