【Luogu P3980】[NOI2008] 志愿者招募

志愿者招募

链接:

洛谷

题目大意:

\(i\) 天要 \(a_i\) 个人。第 \(j\) 种人从第 \(s_j\) 天干到第 \(t_j\) 天,要花 \(c_j\) 元。

找到一种方案使得付出的钱最少。

正文:

这是一种线性规划类问题,最小化 \(\sum_{i=1}^m x_ic_i\),且满足:

\[\left\{\begin{matrix} \sum_{i=1}^m [s_i\leq1\leq t_i]x_i &\geq a_1 \\ \sum_{i=1}^m [s_i\leq2\leq t_i]x_i &\geq a_2 \\ \vdots \end{matrix}\right.\]

考虑用费用流。在用费用流解决某线性规划类的问题时,可以考虑把约束条件的每行看作一点(本题为第 \(i\) 天),建立 \(n+1\) 个点,那么假设有边 \((u,v)\)。则表示已解决 \(v-1\) 个约束条件,现在要处理第 \(v\) 个。

考虑建模:

  1. \(S\) 连向 \(1\)\(n+1\) 连向 \(T\),流量是 \(+\infty\),费用 \(0\)
  2. \(i\) 连向 \(i+1\),流量是 \(+\infty-a_i\),费用 \(0\)
  3. \(s_i\) 连向 \(t_i+1\),流量 \(+\infty\),费用 \(c_i\)

那么最大流一定是 \(+\infty\),否则无解。

代码:

const int N = 1010, M = 10010;
const ll inf = 1e18;

inline ll Read()
{
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

int n, m, s, t;

struct edge
{
	ll to, w, val, nxt;
}e[(N + M) << 1];
int head[N], tot = 1;

void add(int u, int v, ll w, ll val)
{
	e[++tot] = (edge) {v, w, val, head[u]}, head[u] = tot;
	e[++tot] = (edge) {u, 0, -val, head[v]}, head[v] = tot;
}

int pre[N];
bool vis[N];
ll dis[N], incf[N];
queue <int> q;
bool SPFA()
{
	memset (dis, 0x3f3f3f3f, sizeof dis);
	while (!q.empty()) q.pop();
	q.push(s);
	vis[s] = 1; dis[s] = 0; incf[s] = inf;
	while (!q.empty())
	{
		int u = q.front(); q.pop();
		vis[u] = 0;
		for (int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].to;
			if (dis[v] > dis[u] + e[i].val && e[i].w)
			{
				dis[v] = dis[u] + e[i].val;
				pre[v] = i, incf[v] = min(incf[u], e[i].w);
				if (!vis[v]) vis[v] = 1, q.push(v);
			} 
		}
	}
	return dis[t] < inf;
}

ll MCMF()
{
	ll cost = 0;
	while (SPFA())
	{
		int u = t; cost += dis[t] * incf[t];
		for (; u != s; u = e[pre[u] ^ 1].to)
			e[pre[u]].w -= incf[t],
			e[pre[u] ^ 1].w += incf[t];
	}
	return cost;
}

int main()
{
	n = Read(), m = Read(), s = n + 2, t = s + 1;
	add(s, 1, inf, 0); 
	for (int i = 1; i <= n; i++)
	{
		ll val = Read();
		add(i, i + 1, inf - val, 0);
	}
	for (int i = 1; i <= m; i++)
	{
		int u = Read(), v = Read(); ll w = Read();
		add(u, v + 1, inf, w);
	}
	add(n + 1, t, inf, 0);
	printf ("%lld\n", MCMF());
	return 0;
}
posted @ 2021-07-18 11:56  Jayun  阅读(17)  评论(0编辑  收藏  举报