ABC164_E 分层图

二维费用Dijkstra

在每个点要考虑点权\(c[i]\)影响当前可达的位置以及当前点的转换代价为\(d[i]\)
从上限看,如果超过了\(lim = 50 * (n - 1)\)最大银币需求那么就是简单跑一遍\(Dijkstra\)
所以要考虑的点就是当前节点携带的银币总量,记录一下这个状态作为\(dis[i][j]\)把问题化成我们常见的模型,用来跑朴素的\(Dijkstra\),不断"绕圈"并且把状态截止到\(dis[i][lim]\)停下.
边数\(E = n\cdot (lim + 1) + 2m\cdot (lim + 1),\)点数\(V = n\cdot (lim + 1)\)
跑一遍\(Dijkstra\)的复杂度是\(O((V + E)logV)\),足够。
具体的代码细节会注释一下

#include <bits/stdc++.h>
#define fi first
#define se second
#define endl '\n'
#define pii pair<int, int>
#define int long long
using namespace std;
using ll = long long;
using LD = long double;

int T;
const int mod = 998244353;
const ll inf = 3e18;
int n, m, s;

struct node {
  int v, coin, w;
  bool operator<(const node &u) const { return w > u.w; }
};
struct edge {
  int v, cost, w;
};
const int N = 55, lim = 2450;
int dis[N][lim + 5], c[N], d[N];
bool vis[N][lim + 5];
vector<edge> g[N];
void solve() {
  cin >> n >> m >> s;
  for (int i = 1; i <= m; ++i) {
    int u, v, coin, w;
    cin >> u >> v >> coin >> w;
    g[u].push_back({v, coin, w});
    g[v].push_back({u, coin, w});
  }
  for (int i = 1; i <= n; ++i) {
    cin >> c[i] >> d[i];
  }

  for (int i = 1; i <= n; ++i) {
    for (int j = 0; j <= lim; ++j) {
      dis[i][j] = inf;
    }
  }
  //状态限定到lim内
  if (s > lim)
    s = lim;
  priority_queue<node> hp;
  hp.push({1, s, dis[1][s] = 0});

  while (hp.size()) {
    auto [u, coin, dist] = hp.top();
    hp.pop();
    if (dist < dis[u][coin])
      continue;
    if (vis[u][coin])
      continue;
    vis[u][coin] = 1;
	//1.尝试进行金币转银币的操作
	//对当前点进行绕圈
    int ncoin = coin + c[u];
    if (ncoin > lim) {
      ncoin = lim;
    }
    if (dis[u][ncoin] > dist + d[u]) {
      dis[u][ncoin] = dist + d[u];
      hp.push({u, ncoin, dis[u][ncoin]});
    }
   //正常跑Dijkstra
   for (auto [v, cost, w] : g[u]) {
      if (cost > coin)
        continue;
      int ncoin = coin - cost;
      if (dis[v][ncoin] > dist + w) {
        dis[v][ncoin] = dist + w;
        hp.push({v, ncoin, dis[v][ncoin]});
      }
    }
  }
  for (int i = 2; i <= n; ++i) {
    int ans = inf;
    for (int j = 0; j <= lim; ++j)
      ans = min(ans, dis[i][j]);
    cout << ans << "\n";
  }
}
signed main() {
  cin.tie(nullptr)->sync_with_stdio(0);
  T = 1;
  // cin >> T;
  while (T--)
    solve();
  return 0;
}
posted @ 2026-02-28 14:08  Lappybreeze  阅读(23)  评论(0)    收藏  举报