見出し画像

[ABC320] F - Fuel Round Trip

[Q] https://atcoder.jp/contests/abc320/tasks/abc320_f

考察
1. NとHが300なので、DP[300][300][300]でできないか考える。
2. 「ガソリンスタンドが1度しか使えない」の条件が厳しすぎる。bitset<300>で使ったスタンドを管理する、とかは無理そう。
3. なので、復路のときに、そのガソリンスタンドを使ったかどうか考える必要をなくしたい。ガソリンスタンドを見るときに、1回で往路と復路を同時に見たい。
4. ガソリンスタンドのindexを進めるごとに、
a. 往路では、残っているガソリンの量を管理
b. 復路では、必要なガソリンの量を同時に管理する。
5. DP[300][300][300] // DP[index][往路で残っているガソリンh0][復路で必要なガソリンh1]で管理すればできそう
6. 答えは折り返し地点のindexNについて、
DP[N][往路のh0][復路のh1]のうち、h0>=h1であるDPの最小値を求める。

入力例1
4 10 // N H
2 5 9 11 // X
8 10 // P F
5 8
4 9

初期値
DP[0][10][0]:0 // 地点0において、往路のガソリン10 復路で必要なガソリンは0

X[0] = 2
DP[1][8][0]:8 // 復路でガソリン補充。復路で地点2に到達した時点で、必要なガソリンは0
DP[1][8][2]:0 // 補充しない
DP[1][10][2]:8 // 往路でガソリン補充した。往路のガソリンは10

X[1] = 5
DP[2][5][0]:5
DP[2][5][3]:8
DP[2][5][5]:0 // 補充しない
DP[2][7][0]:13
DP[2][7][5]:8
DP[2][10][3]:13
DP[2][10][5]:5

X[2] = 9
DP[3][1][0]:4
DP[3][1][4]:5
DP[3][1][7]:8
DP[3][1][9]:0 // 補充しない 残りガソリン1で、9->11に進むのに2必要だから、このデータは淘汰される
DP[3][3][0]:12
DP[3][3][4]:13
DP[3][3][9]:8
DP[3][6][0]:9
DP[3][6][7]:13
DP[3][6][9]:5
DP[3][10][4]:9
DP[3][10][7]:12
DP[3][10][9]:4

X[3] = 11 // 折り返し地点 ガソリンの補充を考慮しない
DP[4][1][2]:12
DP[4][1][6]:13
DP[4][4][2]:9
DP[4][4][9]:13
DP[4][8][6]:9
DP[4][8][9]:12

解答の候補になるのは、往路のガソリン>=復路で必要なガソリンである、以下の2つ
DP[4][4][2]:9
DP[4][8][6]:9

最小値の9が答え。

実装

ll DP[301][301][301]; // idx, 往路の残りH0, 復路で必要なh1

int main() {
  cincout();

  ll N, H;
  cin >> N >> H;
  vector<ll> X(N), P(N - 1), F(N - 1);
  rep(i, N) cin >> X[i];
  rep(i, N - 1) { cin >> P[i] >> F[i]; }

  rep(i, N + 1) rep(j, H + 1) rep(k, H + 1) DP[i][j][k] = oo;
  // 初期値
  rep(i, H + 1) DP[0][H][0] = 0; // 往路はガソリンH, 復路の0地点での必要なガソリンは0
  rep(i, N) {
    ll dis = X[i];
    if (i > 0) dis -= X[i - 1];
    for (ll h0 = H; h0 >= 0; --h0) {
      for (ll h1 = 0; h1 <= H; ++h1) {
        if (DP[i][h0][h1] == oo) continue;
        if (h0 - dis >= 0 && h1 + dis <= H) { // 往路で進んでガソリンが残っている && 復路で進んでガソリンが足りる
          chmin(DP[i + 1][h0 - dis][h1 + dis], DP[i][h0][h1]); // なんも補給しない
          if (i < N - 1) {
            // 往路で補給する
            chmin(DP[i + 1][min(H, h0 - dis + F[i])][h1 + dis], DP[i][h0][h1] + P[i]);
            // 復路で補給する
            chmin(DP[i + 1][h0 - dis][max(0LL, h1 - F[i] + dis) ], DP[i][h0][h1] + P[i]);
          }
        }
      }
    }
  }
  ll ans = oo;
  rep(i, H + 1) for (ll j = 0; j <= i; ++j) { // i:往路で残ったガソリン <= j:復路で必要なガソリン
    if (DP[N][i][j] == oo) continue;
    chmin(ans, DP[N][i][j]);
  }
  auto debug = [&]() {
    rep(i, N + 1) rep(j, H + 1) rep(k, H + 1) {
      if (DP[i][j][k] == oo) continue;
      cerr << "DP[" << i << "][" << j << "][" << k << "]:" << DP[i][j][k]
           << endl;
    }
  };
  //debug();
  if (ans == oo) ans = -1;
  cout << ans << endl;
}

https://atcoder.jp/contests/abc320/submissions/45650244






この記事が気に入ったらサポートをしてみませんか?