• 月赛
  • 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 条评论

  • 1