- 月赛
Panzx 的 T3粮商鬼才(疑似错题) 正解(疑似错解)
- @ 2026-7-4 7:58:30
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Edge {
int v;
ll w;
};
/* Pareto 剪枝:保留不被任何其他状态严格支配的状态
* 按 grain 降序排序后,money 必须严格递增,否则被前面的状态支配 */
vector<pair<ll, ll>> prune(vector<pair<ll, ll>>& states) {
if (states.empty()) return {};
sort(states.begin(), states.end(), [](const pair<ll, ll>& a, const pair<ll, ll>& b) {
if (a.first != b.first) return a.first > b.first; // grain 降序
return a.second > b.second; // money 降序
});
vector<pair<ll, ll>> res;
ll max_money = -1;
for (auto& [g, m] : states) {
if (m <= max_money) continue; // 存在某个状态 grain' >= g 且 money' > m,当前被支配
res.push_back({g, m});
max_money = m;
}
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N;
ll P;
cin >> N >> P;
vector<ll> W1(N + 1), W2(N + 1);
for (int i = 1; i <= N; ++i) cin >> W1[i];
for (int i = 1; i <= N; ++i) cin >> W2[i];
int M;
cin >> M;
vector<vector<Edge>> adj(N + 1);
vector<int> indeg(N + 1, 0);
for (int i = 0; i < M; ++i) {
int u, v;
ll w;
cin >> u >> v >> w;
adj[u].push_back({v, w});
indeg[v]++;
}
// 拓扑排序
queue<int> q;
for (int i = 1; i <= N; ++i)
if (indeg[i] == 0) q.push(i);
vector<int> topo;
topo.reserve(N);
while (!q.empty()) {
int u = q.front(); q.pop();
topo.push_back(u);
for (auto& e : adj[u])
if (--indeg[e.v] == 0) q.push(e.v);
}
// dp[u]:在国家 u 完成"必须行动"后的 Pareto 最优状态集合
// cand[u]:所有前驱传来的候选状态(尚未剪枝)
vector<vector<pair<ll, ll>>> dp(N + 1);
vector<vector<pair<ll, ll>>> cand(N + 1);
// 起点 1:必须行动一次(买 或 卖)
vector<pair<ll, ll>> init;
init.push_back({0, P}); // 选择卖(0 箱粮,金钱不变)
if (W1[1] > 0) init.push_back({P / W1[1], P % W1[1]}); // 选择买
dp[1] = prune(init);
ll ans = P; // 可以在起点结束
for (int u : topo) {
if (u != 1) {
dp[u] = prune(cand[u]);
cand[u].clear();
}
// 路径可以在 u 结束
for (auto& [g, m] : dp[u]) ans = max(ans, m);
if (dp[u].empty()) continue;
// 向出边传播:先付路费,再在邻居 v 执行"必须行动"
for (auto& e : adj[u]) {
int v = e.v;
ll w = e.w;
for (auto& [g, m] : dp[u]) {
if (m < w) continue; // 付不起路费
ll am = m - w; // 到达 v 时的金钱
ll ag = g; // 到达 v 时的粮食
// 在 v 必须行动:二选一
// 选择买(花光钱买尽可能多的粮)
if (W1[v] > 0) {
ll buy = am / W1[v];
cand[v].push_back({ag + buy, am % W1[v]});
} else {
cand[v].push_back({ag, am}); // W1[v]=0 时避免除零,视为买 0 箱
}
// 选择卖(卖光所有粮)
cand[v].push_back({0, am + ag * W2[v]});
}
}
}
cout << ans << "\n";
return 0;
}
1 条评论
-
徐浩然 LV 3 @ 2026-7-5 11:24:59所以这题到底是不是错题?
- 1