Problem:
ぬいぐるみの整理 (Plush Toys)

問題

ある JOI 関係者は，おもちゃ屋で働いている．今日は，店内にあるぬいぐるみコーナーの整理をすることになった．

ぬいぐるみコーナーの棚には，N 個のぬいぐるみが左から右に一列に並べられている．棚は仕切りにより N 個の区画に区切られており，1 つの区画に 1 個のぬいぐるみを置く．このおもちゃ屋は合計 M 種類のぬいぐるみを売っており，それぞれ 1 から M までの番号が付けられている．棚に並べられた N 個のぬいぐるみは，それぞれこの M 種類のうちのいずれかである．また，それぞれの種類のぬいぐるみは，少なくとも 1 個は存在する．

見栄えを良くするため，同じ種類のぬいぐるみが全て連続して棚に置かれるように，ぬいぐるみを並べ替えたい．次のような方法で，ぬいぐるみを並べ替えることにした．

N 個のぬいぐるみのうちいくつかを選び，棚から取り出す．取り出さなかったぬいぐるみの位置は動かさない．

取り出したぬいぐるみを，好きな順に棚の空いている区画に戻していく．

並べ替えた後，同じ種類のぬいぐるみが全て連続して棚に置かれていなければならない．
並べ替えるために取り出すぬいぐるみの個数の最小値を求めるプログラムを作成せよ．

入力

入力は 1 + N 行からなる．

1 行目には 2 個の整数 N, M (1 ≦ N ≦ 100000, 1 ≦ M ≦ 20) が空白を区切りとして書かれており，ぬいぐるみが N 個あり，種類が M 種類あることを表す．

続く N 行のそれぞれには，1 以上 M 以下の整数が書かれている． N 行のうちの i 行目 (1 ≦ i ≦ N) に書かれた整数は，棚の左から i 番目の区画に置かれたぬいぐるみの種類を表す．各種類について，少なくとも 1 個のぬいぐるみが存在していることが保証される．

出力

並べ替えるために取り出すぬいぐるみの個数の最小値を 1 行で出力せよ．

入出力例

入力例 1

7 2
1
2
2
2
1
2
1

出力例 1

2

入力例 2

12 4
1
3
2
4
2
1
2
3
1
1
3
4

出力例 2

7

入力例 1 においては，最初に置かれているぬいぐるみの種類は左から順に 1， 2， 2， 2， 1， 2， 1 である．並べ替えるために取り出すぬいぐるみの個数を最小にするには，左から 1 番目と 6 番目のぬいぐるみを取り出し，左から 1 番目に種類 2 のぬいぐるみを，左から 6 番目に種類 1 のぬいぐるみを配置すればよい．このとき，取り出すぬいぐるみの個数は 2 個である．

情報オリンピック日本委員会作 『第 16 回日本情報オリンピック JOI 2016/2017 予選競技課題』

Solution in C++14 (judge verdict: Accepted)


#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

#define REP(i, a, b) for (int i = int(a); i < int(b); i++)
#ifdef _DEBUG_
#define dump(val) cerr << __LINE__ << ":\t" << #val << " = " << (val) << endl
#else
#define dump(val)
#endif

using namespace std;

typedef long long int ll;

template<typename T>
vector<T> make_v(size_t a, T b) {
    return vector<T>(a, b);
}
template<typename... Ts>
auto make_v(size_t a, Ts... ts) {
    return vector<decltype(make_v(ts...))>(a, make_v(ts...));
}

template<typename T>
void chmin(T &a, T b) {
    a = min(a, b);
}

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n, m;
    cin >> n >> m;
    vector<int> Cnt(m, 0);
    auto sum = make_v(n + 1, m, 0);
    REP(i, 0, n) {
        int a;
        cin >> a;
        Cnt[a - 1]++;
        sum[i + 1][a - 1]++;
        REP(j, 0, m) {
            sum[i + 1][j] += sum[i][j];
        }
    }

    int size = 1 << m;
    const int inf = 1 << 30;
    auto dp = make_v(size, inf);
    dp[0] = 0;
    REP(i, 0, size) {
        int bs = 0;
        REP(j, 0, m) {
            if ((i >> j) & 1) {
                bs += Cnt[j];
            }
        }
        REP(j, 0, m) {
            if ((i >> j) & 1) continue;
            int nbs = bs + Cnt[j];
            int diff = sum[nbs][j] - sum[bs][j];
            int len = Cnt[j];
            chmin(dp[i | (1 << j)], dp[i] + len - diff);
        }
    }
    cout << dp[size - 1] << endl;
    return 0;
}