Farm Tour POJ - 2135 费用流

题解

要求从起点到终点在回到起点,可以看做起点到终点流量为2的网络流。题目所给出的边没有方向设置为两条容量为1的有向边。
限制流大小为2,最后跑最小费用最大流即可。

AC代码

#include 
#include 
#include 
#include 
#include 
#define fst first
#define sed second
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e3 + 10;
const int M = 1e4 + 10;
int n, m, st, ed;
bool vis[N]; //在队列的节点
int dis[N], pre[N]; //pre最短路前驱通往当前点的边

struct edge
{
	int v, w, c, nxt; //w容量 c代价
	edge() {}
	edge(int vv, int ww, int cc, int nx) : v(vv), w(ww), c(cc), nxt(nx) {}
}e[M * 4];
int h[N], idx;
void AddEdge(int u, int v, int w, int c)
{
	e[idx] = edge(v, w, c, h[u]);
	h[u] = idx++; //后++编号0到idx-1 结束为-1
}
bool SPFA(int st, int ed) //每个点相对于st的层次 也就是边权为1的最短路
{
	queue<int> q;
	memset(dis, 0x3f, sizeof(dis));
	memset(pre, -1, sizeof(pre));
	dis[st] = 0;
	vis[st] = 1;
	q.push(st);
	while (!q.empty())
	{
		int u = q.front(); q.pop(), vis[u] = 0;
		for (int i = h[u]; ~i; i = e[i].nxt)
		{
			int v = e[i].v, w = e[i].w, c = e[i].c; //w流量 c代价
			if (dis[v] > dis[u] + c && w) //代价更低且未满流
			{
				dis[v] = dis[u] + c; //更新代价
				pre[v] = i; //记录边编号
				if (!vis[v]) //不在队伍内则入队
					q.push(v), vis[v] = 1;
			}
		}
	}
	return pre[ed] != -1; //返回st是否和ed连通
}
void MCMF(int st, int ed, int &cost)
{
	cost = 0;
	int free = 2; //剩余所需流量 回路流量为2即可
	while (free && SPFA(st, ed))
	{
		int mi = free;
		for (int i = pre[ed]; ~i; i = pre[e[i ^ 1].v])
			mi = min(mi, e[i].w);
		for (int i = pre[ed]; ~i; i = pre[e[i ^ 1].v])
		{
			e[i].w -= mi, e[i ^ 1].w += mi;
			cost += mi * e[i].c; //流量*当前边代价
		}
		free -= mi;
	}
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	memset(h, -1, sizeof(h));
	cin >> n >> m;
	for (int i = 0; i < m; ++i)
	{
		int u, v, c;
		scanf("%d%d%d", &u, &v, &c);
		AddEdge(u, v, 1, c);
		AddEdge(v, u, 0, -c); //反向边和正向连续建立!!
		AddEdge(v, u, 1, c); //无向图双向边 反向0容量负权
		AddEdge(u, v, 0, -c);
	}
	int cost = 0;
	MCMF(1, n, cost); //1到n再回来
	printf("%d\n", cost);

	return 0;
}

你可能感兴趣的:(___图论___,费用流)