1.题目描述:点击打开链接
2.解题思路:本题应该是MCMF的经典例题,利用拆点法解决。但这种经典例题的确初学的时候不好建模,还是学学别人的代码吧。本题要求找两条不相交的路径(不包括起点和终点),且两条路径的权和相加最小。如果直接套用模板解题,可能会出现路径相交的情况,为什么呢?因为MCMF中找最短路时只是边不会被重复选取,并没有涉及结点是否重复。因此为了让结点不重复选取,可以将一个结点i拆成i和i',i为入结点,i'为出结点,边i->i'的容量是1(容量设为1的好处是如果选了这条边,那么流量恰好是1,没有选到流量恰好是0),费用是0,这样,如果某条路径经过i点,必然会经过i->i‘这条路径,由于路径只能选一次,因此以后再找最短路时就不会选到i->i'这条路,从而不会再次选到i点。这就是拆点法的巧妙之处!
因此本题的要点就是合理的给i和i'编号,令i的编号范围是1~n-2,i’的范围是n~2*n-3(例如结点2拆后的编号分别是1,n;结点3是2,n+1,以此类推)。起点1的编号就是0,终点n的编号就是n-1。再令出结点i'到入结点j的边的容量是1,费用是w。因此只需要求出0-n-1的最小费用最大流即可。
3.代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;
#define N 2000+10
#define INF 100000000
typedef long long LL;
struct Edge
{
int from, to, cap, flow, cost;
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>edges;
vector<int>G[N];
int inq[N];//是否在队列中
int d[N];//Bellman-Ford
int p[N];//上一条弧
int a[N];//可改进量
void init(int n)
{
this->n = n;
for (int i = 0; i<n; i++)
G[i].clear();
edges.clear();
}
void addedge(int from, int to, int cap, int cost)
{
edges.push_back(Edge( from, to, cap, 0, cost ));
edges.push_back(Edge( to, from, 0, 0, -cost ));
m = edges.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
bool BellmanFord(int s, int t, int&flow, long long&cost)
{
for (int i = 0; i<n; i++)
d[i] = INF;
memset(inq, 0, sizeof(inq));
d[s] = 0, inq[s] = 1, p[s] = 0, a[s] = INF;
queue<int>q;
q.push(s);
while (!q.empty())
{
int u = q.front(); q.pop();
inq[u] = 0;
for (int i = 0; i<G[u].size(); i++)
{
Edge&e = edges[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]){ q.push(e.to); inq[e.to] = 1; }
}
}
}
if (d[t] == INF)
return false;
flow += a[t];
cost += (long long)d[t] * a[t];
int u = t;
while (u != s)
{
edges[p[u]].flow += a[t];
edges[p[u] ^ 1].flow -= a[t];
u = edges[p[u]].from;
}
return true;
}
int MincostMaxflow(int s, int t, int flow_limit,long long&cost)
{
int flow = 0;
cost = 0;
while (flow<flow_limit&&BellmanFord(s, t, flow, cost));
return flow;
}
}g;
int main()
{
//freopen("t.txt", "r", stdin);
int n, m;
while (scanf("%d%d", &n, &m) == 2 && n)
{
g.init(2 * n - 2);
for (int i = 2; i <= n - 1; i++)//2到n-1中i和i'的编号分别为1……n-2,n……2*n-3
g.addedge(i - 1, n - 2 + i, 1, 0);//i->i'相连
for (int i = 0; i < m; i++)
{
int u, v, w;
cin >> u >> v >> w;
if (u != 1 && u != n)u += n - 2;//u都替换成i'的编号
else u--;//端点仍然是i的编号
v--;//v仍然是i的编号
g.addedge(u, v, 1, w);//i'->j相连
}
LL cost;
g.MincostMaxflow(0, n - 1, 2, cost);
printf("%lld\n", cost);
}
return 0;
}