【AtCoder】ABC 466 E - Range Flip

実行時間制限: 2 sec / メモリ制限: 1024 MiB / Difficulty: 1027 / NoviSteps: 1Q / 配点: 450 点
問題概要
枚のカードが並んでいて、カードには の番号が付けられている。 カード の表面には整数 が、裏面には整数 が書かれており、はじめ、すべてのカードは表面が上を向いている。
これから、以下の操作を高々 回行うことができる。
- なる整数 を選ぶ。 なる各整数 について、カード を裏返す。
操作を終えた後、各カードの上を向いている面に書かれている数の総和として考えられる最大値を求めよ。
制約
- 入力される値はすべて整数
考察
問題の言い換え
まず、重要な考察として、「重複した区間の操作は考える必要がない」ということが挙げられる。
例えば、 という2回の操作を行った場合、カード は2回裏返されることになり、元の状態に戻ってしまう。 つまり、これらの操作は、 という2回の操作と同じ結果になる。
したがって、高々 回の区間反転によって裏面になるカードの集合は、必ず高々 個の互いに重ならない区間で表すことができ、逆に、高々 個の互いに重ならない区間をそれぞれ1回ずつ裏返せば、その状態を実現できる。
また、カード を裏返すことによって変化する 枚のカードの総和は、 と書ける。
したがって、この問題は
- 長さ の数列 が与えられるので、高々 個の互いに重ならない区間を選び、その区間に含まれる整数の総和を最大化せよ。
と言い換えられる。
動的計画法で解く
言い換えた問題は、動的計画法によって解くことができる。
ここでは、以下の2つのDPテーブルを定義する。
- ここまでに 個の区間を選び、カード を裏返さない場合の の総和の最大値
- ここまでに 個の区間を選び、カード を裏返す場合の の総和の最大値
初期値は、 であり、他の値は とする。
各 について、以下の遷移を行う。
- カード を裏返さない場合
- カード 共に裏返さない場合と、 個目の区間をカード までで終了させる場合の2パターンを考える(カード の状態は関係ない)。
- カード を裏返す場合
- カード を 個目の区間の最初のカードとして裏返す場合と、 個目の区間に含める場合の2パターンを考える。
言い換えた問題の答えは であり、最終的な答えはこれに を加えた値となる。
各カードについて、区間数 を考えるため、計算量は となる。
実装例
DPテーブルを使いまわすことで、空間計算量を に削減することができる。
1.#include <bits/stdc++.h>2.using namespace std;3. 4.#define rep(i, start, end) for (auto i = (start); (i) < (end); (i)++)5. 6.constexpr long long INFL = 1e+18;7. 8.using ll = long long;9. 10.template <typename T>11.inline bool chmax(T& a, T b) { return ((a < b) ? (a = b, true) : (false)); }12. 13.// ======================================== //14. 15.int main()16.{17. int N, K;18. cin >> N >> K;19. vector<ll> A(N), B(N);20. ll ans = 0;21. rep(i, 0, N) cin >> A[i] >> B[i], ans += A[i];22. 23. vector<ll> dp0(K + 1, -INFL), dp1(K + 1, -INFL);24. dp0[0] = 0;25. rep(i, 0, N) {26. ll diff = B[i] - A[i];27. 28. vector<ll> ndp0(K + 1, -INFL), ndp1(K + 1, -INFL);29. rep(j, 0, K + 1) {30. ndp0[j] = max(dp0[j], dp1[j]);31. 32. if (j >= 1) {33. ndp1[j] = max(dp0[j - 1], dp1[j]) + diff;34. }35. }36. 37. swap(ndp0, dp0);38. swap(ndp1, dp1);39. }40. 41. ll add = 0;42. rep(i, 0, K + 1) chmax(add, max(dp0[i], dp1[i]));43. 44. cout << ans + add << endl;45. 46. return 0;47.}実装時間: 30分
コメント
最初の問題の言い換えが難しい。





