---
# System prepended metadata

title: IOI2007 Day2-1 坑夫 (Miners)
tags: [IOI]

---

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