CF1937 E Pokémon Arena 題解

Martian148發表於2024-03-14

Question

CF1937 E Pokémon Arena

Solution

算是一個比較典的題目,這個題目很容易轉化成圖論,初始從 \(1\) 號點走到 \(n\) 號,每個點的邊權是相差最小的那個屬性之差

這樣子建圖的時間複雜度是 \(O(n^2m)\)

考慮最佳化建圖

  • 拆點,將一個點拆成兩個點,其之間的邊權為 \(c_i\)
  • 前字尾最佳化建圖,發現有 \(m\) 中屬性,每個屬性有 \(n\) 中不同的取值,暴力建邊需要 \(O(n^2m)\),但是如果對於每個屬性,先將每個寶可夢的屬性值排序,相鄰的建邊就可以不用建很多邊了,而且差值是可以累加的

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const LL INF = 0x3f3f3f3f3f3f3f3f;

void solve() {
    int n, m;
    cin >> n >> m;
    vector<int> c(n);
    for (int i = 0; i < n; i++) cin >> c[i];
    vector<vector<int> > a(n, vector<int>(m));
    for (int i = 0; i < n; i++) 
        for (int j = 0; j < m; j++) 
            cin >> a[i][j];

    auto get = [&] (int x, int y) {
        return x * n + y;
    };

    int t = 2 * n + n * m;
    vector<vector<pii> > g(t);
    for (int i = 0; i < n; i++) 
        g[i].push_back({i + n, c[i]});
    vector<int> id(n);
    iota(id.begin(), id.end(), 0);
    for (int i = 0; i < m; i ++) {
        sort (id.begin(), id.end(), [&](int x, int y) {
            return a[x][i] < a[y][i];
        });
        for (int j = 0; j + 1 < n; j++) {
            g[2 * n + get(i, j)].push_back({2 * n + get(i, j + 1), 0});
            g[2 * n + get(i, j + 1)].push_back({2 * n + get(i, j), - a[id[j]][i] + a[id[j + 1]][i]});
        }
        for (int j = 0; j < n; j++) {
            g[2 * n + get(i, j)].push_back({id[j], 0});
            g[id[j] + n].push_back({2 * n + get(i, j), 0});
        }
    }

    vector<LL> dis(t, INF); vector<bool> vis(t, 0);
    priority_queue<pair<LL,int> , vector<pair<LL,int> >, greater<pair<LL,int> > > q; 
    dis[n] = 0; q.push({0, n}); 
    while (!q.empty()) {
        auto [d, u] = q.top(); q.pop();
        if (vis[u]) continue;
        vis[u] = 1;
        for (auto [v, w] : g[u]) {
            if (dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                q.push({dis[v], v});
            }
        }
    }
    cout << dis[2 * n - 1] << '\n';
}

int main() {
    freopen ("E.in" ,"r", stdin);
    ios::sync_with_stdio();
    cin.tie(0); cout.tie(0);

    int T; cin >> T;
    while (T--) solve();
    return 0;
}

相關文章