例题11-9 海军上将 UVa1658

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

你可能感兴趣的:(最小费用最大流,Bellman-Ford)