【洛谷2469/BZOJ1927】[SDOI2010]星际竞速(费用流/最小路径覆盖)

题目:

洛谷2469

分析:

把题目翻译成人话:给一个带边权的DAG,求一个路径覆盖方案使路径边权总和最小。从点\(i\)开始的路径需要额外加上\(A_i\)的权值。
回xian忆chang一xue下xi不带权DAG的最小路径覆盖用网络流是怎么做的:把点\(u\)拆成\(u\)\(u'\)两个点,如果原图存在边\((u,v)\)就在网络中连边\((u,v')\),然后源点\(s\)向所有\(u\)连边,所有\(v\)向汇点\(t\)连边,所有边容量均为\(1\),跑最大流。原图中点数\(n\)减去最大流就是最小路径覆盖。
考虑这样做的原理:最小路径覆盖中每个点属于且仅属于一条路径。对于任意包含多于\(1\)个点的路径,有且只有一个点入度为\(0\),一个点出度为\(0\),其余所有点入度、出度皆为\(1\)。网络中若\((s,u)\)有流量说明\(u\)出度为\(1\)\(s\)流到\(u\)后必然要流向某个点\(v'\)),若\((u',t)\)有流量说明\(u\)入度为\(1\)(必须有某个点流到\(u'\)\((u',t)\)才有流量)。那么最大流就是所有点入度之和,\(n\)减去入度之和就是入度为\(0\)的点数,即路径起点的数量,即路径数。如果边\((u,v')\)有流量说明原图中\((u,v)\)这条边在最小路径覆盖中。
那么给每条边加上权以后呢?自然能想到把原图中边\((u,v)\)的权作为网络中\((u,v')\)的费用然后跑费用流

然后你就gg了

费用流全名叫“最小费用最大流”,是在保证流量最大的情况下的最小费用。在这道题中,就相当于首先要保证路径的条数最少(即流量最大),然后再使费用最小。此时要感谢某神犇T兔z崽z子给我说的我自己想出来的方法……先打个广告

戳我进入兔崽子的博客

\(s\)向每个\(u'\)连边,这条边有流量说明\(u\)入度为\(0\)(因为\((u',t)\)容量为\(1\)\((s,u‘)\)有流量就说明肯定没有别的点会流到\(u'\))。这样无论如何路径覆盖最大流都是\(n\),就消除了路径条数的影响,求出最小费用即为答案。
这种方案顺便解决了下一个问题:路径起点有额外权值。把\(A_u\)作为\((s,u')\)的费用即可。

代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
namespace zyt
{
	typedef long long ll;
	const ll INF = 0x3f3f3f3f3f3f3f3fLL;
	const int N = 810, P = N * 2, M = (N * 3 + 15010) * 2;
	int head[P], cnt, n, m, s, t;
	struct edge
	{
		int to, w, c, next;
	}e[M];
	inline void add(const int a, const int b, const int c, const int d)
	{
		e[cnt] = (edge){b, c, d, head[a]};
		head[a] = cnt++;
	}
	inline void addtw(const int a, const int b, const int c, const int d)
	{
		add(a, b, c, d);
		add(b, a, 0, -d);
	}
	namespace EK
	{
		bool vis[P];
		ll dis[P];
		int pre[P];
		bool SPFA()
		{
			queue<int>q;
			memset(pre, -1, sizeof(pre));
			memset(vis, 0, sizeof(vis));
			memset(dis, INF, sizeof(dis));
			q.push(s);
			vis[s] = true;
			dis[s] = 0;
			while (!q.empty())
			{
				int u = q.front();
				q.pop();
				vis[u] = false;
				for (int i = head[u]; ~i; i = e[i].next)
				{
					int v = e[i].to;
					if (e[i].w && dis[v] > dis[u] + e[i].c)
					{
						dis[v] = dis[u] + e[i].c, pre[v] = i;
						if (!vis[v])
							vis[v] = true, q.push(v);
					}
				}
			}
			return dis[t] != INF;
		}
		ll EK()
		{
			ll ans = 0;
			while (SPFA())
			{
				int i = pre[t];
				int minn = n + 1;
				while (~i)
					minn = min(minn, e[i].w), i = pre[e[i ^ 1].to];
				i = pre[t];
				while (~i)
					e[i].w -= minn, e[i ^ 1].w += minn, i = pre[e[i ^ 1].to];
				ans += minn * dis[t];
			}
			return ans;
		}
	}
	int work()
	{
		ios::sync_with_stdio(false);
		memset(head, -1, sizeof(head));
		cin >> n >> m;
		s = n * 2 + 1, t = n * 2 + 2;
		for (int i = 1; i <= n; i++)
		{
			int a;
			cin >> a;
			addtw(s, i, 1, 0);
			addtw(s, i + n, 1, a);
			addtw(i + n, t, 1, 0);
		}
		for (int i = 0; i < m; i++)
		{
			int a, b, c;
			cin >> a >> b >> c;
			if (a > b)
				swap(a, b);
			addtw(a, b + n, 1, c);
		}
		cout << EK::EK();
		return 0;
	}
}
int main()
{
	return zyt::work();
}
posted @ 2018-09-09 17:18  Inspector_Javert  阅读(185)  评论(0编辑  收藏  举报