SRM 518 500pt: ConvexSequence
問題概要
整数列(長さN<50, 要素x<10^9)が与えられる。各要素を減らして凸になるようにしたい。最小いくつ減らす必要があるか求める問題。
考えたこと
- 前2つを覚えてDP?でも前2つのとりうる値の範囲が広すぎてアレだ。
- 実は特徴点が全部列挙できたりしないんだろうか。無理っぽいなあ。
- 最小値をどれにするか全探索して増加列と減少列に分離する?
- 最小値はdecする必要が無いように思うけど、ほんとかなあ?
- じゃあ、両端から狭めていく?
- 区間に関するDP?
- 全部ダメだ。本番は手も足も出ずに完封負けした。
- チャレンジフェーズ。やけに出してる人が多いが見てみるとみんな局所改善の繰り返し。
- 本当に?とおもうより先に、何でみんなこれが見えるの?という驚きが先にきた。
- でも今にして思えば、提出率がやたら高い問題って貪欲っぽい解法が多い気がする。
- もちろん罠で大撃墜祭になることもあるわけだけど。
- 一応greedy解法が正解を与えることと、時間内に終了することはSRM直後のTwitterなどを見て理解したつもり。
- 値が下がるしかないから、この要素はある値より小さくなければダメ、という制約を順次適用していけばよい、という考え方らしい。
- でもこれを自力でちゃんと思いつく自信は全くない。
practiceで通ったコード
#include <algorithm> #include <vector> #include <cstdio> using namespace std; typedef long long int64; struct ConvexSequence { int64 getMinimum(vector <int> a) { const int N = a.size(); int64 ret = 0; for(;;){ bool update = false; for(int i=1; i<N-1; i++){ if(a[i-1] + a[i+1] < 2*a[i]){ int diff = a[i] - (a[i-1] + a[i+1]) / 2; ret += diff; a[i] -= diff; update = true; } } if(!update){ break; } } return ret; } };