Official

D - Step Up Robot Editorial by MMNMM


この問題には、次のような性質があります。

  • いま、ロボットが \(k\) 段目にいるとする。「ここから \(l\) 段目に到達できるかどうか」は、「これまでどのような動き方をしてきたか」と関係ない。

(例えば、「\(0\) 以上の整数が書かれたカードの束を持っていて、カードを捨てるとそこに書かれた段数だけ階段を登ることができる。モチの置いてある段には登れない。」というような問題設定では、上の性質は成り立ちません。これまでに捨てたカードがこれからの行動に影響してしまうためです。)

上の性質を使うと、「いまいる段数」を状態として持つ DP を考えることができます。

次のような DP 配列を定義します。

  • \(\operatorname{dp}[i]\coloneqq i\) 段目に登ることができるなら \(1\) 、できないなら \(0\)

すると、\(\operatorname{dp}[i]\) はモチの位置 \(B _ i\) とより低い段に登れるか \(\operatorname{dp}[0],\operatorname{dp}[1],\ldots,\operatorname{dp}[i-1]\) の情報を用いて計算できます。

具体的には、次のようにして求められます。

\[ \operatorname{dp}[i]=\left\lbrace\begin{matrix}0&\quad&({}^\exists j.\ B _ j=i)\\\displaystyle\bigvee_j \operatorname{dp}[i-A_j]&&(\textrm{otherwise.})\end{matrix}\right. \]

言葉にすると、「モチが設置されていない段は、\(A _ i\) 段前のどれかの段に登れるなら、登れる」です。

はじめは \(0\) 段目にいるので \(\operatorname{dp}[0]=1\) 、\(\operatorname{dp}[-n]=0\ (n\gt0)\) であることに注意して、上の式に従って \(\operatorname{dp}\) を \(i\) の昇順に求めていくことができます。

実装例は以下のようになります。

#include <iostream>
#include <vector>

int main() {
    using namespace std;

    unsigned long N;
    cin >> N;
    vector<unsigned long> A(N);
    for (auto &&a : A)
        cin >> a;

    unsigned long M;
    cin >> M;
    vector<unsigned long> B(M);
    for (auto &&b : B)
        cin >> b;

    unsigned long X;
    cin >> X;
    // dp[i] := i 段目に登れるか
    // available[i] := i 段目にモチがあるか
    vector<unsigned long> dp(X + 1), available(X + 1, 1);
    for (const auto b : B)
        available[b] = 0;

    // 0 段目には登れる
    dp[0] = 1;
    for (unsigned long i{1}; i <= X; ++i)
        if (!available[i])
            // モチがあると、登れない
            dp[i] = 0;
        else
            // モチがないとき、dp[i - a] の or が答え
            for (const auto a : A)
                if (i >= a)
                    dp[i] |= dp[i - a];

    cout << (dp[X] ? "Yes" : "No") << endl;

    return 0;
}

posted:
last update: