CF1851E. Nastya and Potions 题解 DAG上的DP

题目链接:https://codeforces.com/problemset/problem/1851/E

视频讲解地址:https://www.bilibili.com/video/BV1tZ1FYPELp/

解题思路:

首先,可以做一下预处理,因为编号为 \(p_i\) 的药水可以免费无限供应,所以可以直接令所有的 \(c_{p_i} = 0\)

其次,这是一个 DAG。

所以可以把建一个有向图:如果混合得到第 \(u\) 瓶药水需要一瓶第 \(v\) 瓶药水,则在有向图中连一条 \(v \rightarrow u\) 的有向边。

然后你就可以发现,对于第 \(u\) 瓶药水,只有两个选择:

  • 方案一:单独购买第 \(u\) 瓶药水,花费 \(c_u\) 个金币。
  • 方案二:混合得到第 \(u\) 瓶药水,花费的是所有需要的材料的最少花费之和。

即:

定义 \(f_u\) 为得到一瓶第 \(u\) 瓶药水的最小花费,则方案二可以表示为 \(\sum\limits_{v \rightarrow u} f_v\),这里 \(v\) 对应的是所有直接连向 \(u\) 的节点。

同时,入度为 \(0\) 的点只能选择方案一。

我们用 \(d_u\) 表示节点 \(u\) 的入度。

则:

  • \(d_u = 0\),则 \(f_u = c_u\)
  • \(d_u \gt 0\),则 \(f_u = \min( c_u \ , \sum\limits_{v \rightarrow u} f_v )\)

基于上述思路,我们可以用两种方式解决这个问题。

  • 方式一:拓扑排序;
  • 方式二:建反图 + 记忆化搜索。

示例程序1(拓扑排序):

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int T, n, k, c[maxn], d[maxn];
vector<int> g[maxn];
long long f[maxn];

void init() {
	for (int i = 1; i <= n; i++) {
		g[i].clear();
		f[i] = 0;
	}
}

void tp_sort() {
	queue<int> que;
	for (int i = 1; i <= n; i++)
		if (!d[i])
			f[i] = c[i], que.push(i);
	while (!que.empty()) {
		int u = que.front();
		que.pop();
		for (auto v : g[u]) {
			f[v] += f[u];
			if (--d[v] == 0)
				f[v] = min(f[v], 1ll * c[v]),
				que.push(v);
		}
	}
}

int main() {
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &k);
		init();
		for (int i = 1; i <= n; i++)
			scanf("%d", c+i);
		for (int i = 0; i < k; i++) {
			 int p;
			 scanf("%d", &p);
			 c[p] = 0;
		}
		for (int u = 1; u <= n; u++) {
			int v;
			scanf("%d", d+u); // d[u]
			for (int i = 0; i < d[u]; i++) {
				scanf("%d", &v);
				g[v].push_back(u);
			}
		}
		tp_sort(); // 拓扑排序 
		for (int i = 1; i <= n; i++)
			printf("%lld ", f[i]);
		puts("");
	}
	return 0;
}

示例程序2(建反图 + 记忆化搜索):

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int T, n, k, c[maxn], d[maxn];
vector<int> g[maxn];
bool vis[maxn];
long long f[maxn];

void init() {
	for (int i = 1; i <= n; i++) {
		g[i].clear();
		vis[i] = false;
	}
}

long long dfs(int u) {	// 返回 f[u]
	if (vis[u])
		return f[u];
	vis[u] = true;
	f[u] = c[u];
	if (!d[u]) return f[u];
	long long tmp = 0;
	for (auto v : g[u])
		tmp += dfs(v);
	return f[u] = min(f[u], tmp);
}

int main() {
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d", &n, &k);
		init();
		for (int i = 1; i <= n; i++)
			scanf("%d", c+i);
		for (int i = 0; i < k; i++) {
			 int p;
			 scanf("%d", &p);
			 c[p] = 0;
		}
		for (int u = 1; u <= n; u++) {
			int v;
			scanf("%d", d+u); // d[u]
			for (int i = 0; i < d[u]; i++) {
				scanf("%d", &v);
				g[u].push_back(v);
			}
		}
		for (int i = 1; i <= n; i++)
			printf("%lld ", dfs(i));
		puts("");
	}
	return 0;
}
posted @ 2024-10-24 00:10  quanjun  阅读(5)  评论(0编辑  收藏  举报