Portals§
显然可以发现,如果一些单向边指向同一个城市,完全可以将这些单向边缩减成一条单向边,即只取起始点 id 更大的那条边。
不需要多麻烦解释,由于城市是顺序访问,对于某个城市如果你想守护它,不如越靠后越好,过早守护可能会导致后面的城市攻占不下来,运用贪心的思想很容易理解。
我们会使用动态规划对这题进行求解。
设计状态 \(dp_{i,k}\) ,表示第 i 轮拥有 k 个士兵时能得到的最大收益。
易得 \(dp_{i,k} = \begin{cases} dp_{i-1,k-b[i]} , k-b[i] \ge a[i]\\ -1 , k-b[i]<a[i] \end{cases}\)
当然,这一轮还未结束,我们完全可以将手中过多的士兵派走从而获得更多收益。
因此 \(dp_{i,k} = \begin{cases} \max (dp_{i,k+1} + c[j]), dp_{i,k+1} \ne -1 \\ do\ nothing , otherwise \end{cases} , j \in e[i]\)
#include <bits/stdc++.h> constexpr int N = 5000; std::vector<int> a(N), b(N), c(N), dp(N + 1, -1), r(N), e[N]; int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int n, m, k; std::cin >> n >> m >> k; for (int i = 0; i < n; i++) { std::cin >> a[i] >> b[i] >> c[i]; } std::iota(r.begin(), r.end(), 0); while (m--) { int u, v; std::cin >> u >> v; --u, --v; r[v] = std::max(r[v], u); } for (int i = 0; i < n; i++) { if (r[i] > -1) { e[r[i]].push_back(i); } } dp[k] = 0; for (int i = 0; i < n; i++) { for (int j = N - b[i]; j >= a[i]; j--) { dp[j + b[i]] = dp[j]; } for (int j = 0; j < a[i] + b[i]; j++) { dp[j] = -1; } for (int it : e[i]) { for (int j = 0; j < N; j++) { if (dp[j + 1] != -1) { dp[j] = std::max(dp[j], dp[j + 1] + c[it]); } } } } std::cout << *std::max_element(dp.begin(), dp.end()) << '\n'; return 0; }