とても教育的な Greedy 問題!
問題概要
個の非負整数からなる数列
が与えられる。この数列に対して、
「正の整数である要素を 1 つ選び、-1 する」
という操作を繰り返すことで、どの隣接する 2 個の要素の和も 以下となるようにしたい。
これを実現するための最小操作回数を求めよ。
制約
考えたこと
この手の問題では「端から順に考えていく」ことが有効であることが多々ある。今回も左端の 2 要素 と
について考えてみよう。
ここでパッと思いつくことは、 を減らすよりも、
を減らした方が、後々得であるということだ。より詳しく言えば、
を減らしても
という制約にしか効いてこないが、
を減らすと
という制約だけでなく
という制約にも効いてくるのだ。
というわけで、基本的には を減らすこととする。ただし注意点として、
である場合には、
となるまで
を減らす必要がある。
よって、最初の 2 要素 については、次のようにすればよい。
である場合には、
となるまで
を減らす
- その後、
である場合には、
となるまで
を減らす
その後
次に について考える。
そうすると、同様の議論によって、 についての手続きをそのまま踏襲すればよいことに気づく。つまり、
である場合には、
となるまで
を減らす (実際にはこの場合は発生しない)
である場合には、
となるまで
を減らす
とすればよい。それ以降も について、
と
に同様の処理を続けていけばよい。
全体の計算量は となる。
コード
#include <bits/stdc++.h> using namespace std; int main() { long long N, x; cin >> N >> x; vector<long long> a(N); for (int i = 0; i < N; ++i) cin >> a[i]; // 左端から順に見ていく long long res = 0; for (int i = 0; i+1 < N; ++i) { if (a[i] + a[i+1] <= x) continue; // そもそも a[i] > x である場合には a[i] = x になるまで削る if (a[i] > x) { res += a[i] - x; a[i] = x; } // a[i] よりも a[i+1] を下げておいた方が、後にとってより良い long long need = a[i] + a[i+1] - x; // a[i] <= x が保証されるので need >= 0 res += need; a[i+1] -= need; } cout << res << endl; }