--- tags: IOI --- # IOI2007 Day2-1 坑夫 (Miners) DP練習問題 ## 問題 https://www.ioi-jp.org/ioi/2007/problem/day2/miners-prob-j.pdf https://oj.uz/problem/view/IOI07_miners 炭鉱が2つあり、食事がやってくる度にどちらかの炭鉱に届ける。 食事は3種類あり、来る順番が決まっている。 坑夫は食事に変化があることを好むので、炭鉱に食事がやってくる度、直近3回の食事の種類数の分だけ石炭を採掘する。 最大でどれだけ石炭を採掘できるだろうか? $N ≤ 100000$ ## 考察 直近3回しか見ないので、各炭鉱の直近2回分が同じならその先は同一視できる。 よって、 dp[$i$][$j$] := $i$ 番目の食事までを届けた時、各炭鉱の直近2回分の状態が $j$ である場合の最大値 として、$O(N)$ 定数倍重めでDPできる。 ## 実装 各炭鉱の直近2回分の状態を 2bit * 4 で持った。 434ms / 1500ms https://oj.uz/submission/213507 ```cpp #include <bits/stdc++.h> using namespace std; const int INF = 0x3fffffff; #define all(a) begin(a), end(a) #define each(i,a) for(auto&& i : a) #define rep(n) for(int i = 0; i < (n); ++i) template<class T, class U> bool chmax(T& a, const U& b){ if(a < b){ a = b; return 1; } return 0; } inline constexpr int kinds(int a, int b, int c){ if(a > b) swap(a, b); if(b > c) swap(b, c); if(a > b) swap(a, b); int ans = 3; if(!a || a == b) ans--; if(!b || b == c) ans--; return ans; } int main(){ cin.tie(nullptr); ios::sync_with_stdio(false); string s; cin >> s >> s; each(i, s){ if(i == 'B') i = 1; else if(i == 'F') i = 2; else i = 3; } vector<int> dp1(1 << 8, -INF); dp1[0] = 0; each(c, s){ vector<int> dp2(1 << 8, -INF); rep(1 << 8){ int a1 = i >> 6 & 3, a2 = i >> 4 & 3, b1 = i >> 2 & 3, b2 = i & 3; int j = a2 << 6 | c << 4 | b1 << 2 | b2; chmax(dp2[j], dp1[i] + kinds(a1, a2, c)); j = a1 << 6 | a2 << 4 | b2 << 2 | c; chmax(dp2[j], dp1[i] + kinds(b1, b2, c)); } swap(dp1, dp2); } cout << *max_element(all(dp1)) << endl; } ```