---
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;
}
```