けんちょんの競プロ精進記録

競プロの精進記録や小ネタを書いていきます

AtCoder ABC 140 C - Maximal Value (4Q, 灰色, 300 点)

数学嫌いの方は問題文読んだ瞬間に一瞬敬遠心が生まれてしまうかもしれない...でも落ち着けば難しくはない

問題へのリンク

問題概要

長さ  N の数列  A_1, \dots, A_N (具体的な値はわからない) に対して

  •  B_{i} \ge \max(A_{i}, A_{i+1})

を満たす数列  B_{1}, \dots, B_{N-1} が与えられます。 A_1, \dots, A_N の総和として考えられる最大値を求めてください。

制約

  •  1 \le N \le 100

考えたこと: まずは手で様子をつかむ

こういう一見してよくわからないな...と感じた問題は、あれこれ手で実験してみるに限る。

たとえば  B = (1, 4, 3) とかだったら、

  •  A_0 \le 1
  •  A_1 \le 1
  •  A_1 \le 4
  •  A_2 \le 4
  •  A_2 \le 3
  •  A_3 \le 3

でなければならないことがわかる。よってこれを満たす範囲で  A をできるだけ大きくしようと思うと  A = (1, 1, 3, 3) となる。

一般化

この時点で、 A の各要素は両端を除くと「 B の隣り合う要素の最小値以下である」ということがわかる。上の例だと、 A_1 B = (1, 4, 3) のうちの  1 4 の最小値以下であったし、 A_2 4 3 の最小値以下であった。

同様に  A の両端も、 B の両端の値以下であることがわかる。

そして、 A の両端の値を  B の両端の値そのものにして、 A の両端以外の値も  B の隣り合う二要素の最小値そのものにしても、条件を満たしているので、それが最適解である。

まとめると

  •  A_0 = B_0
  •  A_1 = \min(B_0, B_1)
  •  A_2 = \min(B_1, B_2)
  • ...
  •  A_{N-2} = \min(B_{N-3}, B_{N-2})
  •  A_{N-1} = B_{N-2}

とすれば OK。

#include <iostream>
#include <vector>
using namespace std;

int main() {
    int N; cin >> N;
    vector<long long> B(N-1);
    for (int i = 0; i < N-1; ++i) cin >> B[i];

    long long res = B[0] + B.back();
    for (int i = 0; i + 1 < B.size(); ++i) {
        res += min(B[i], B[i+1]);
    }
    cout << res << endl;
}