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; }