はまやんはまやんはまやん

hamayanhamayan's blog

/\/\/\/ [AtCoder Beginner Contest 111 / AtCoder Regular Contest 103 C]

https://beta.atcoder.jp/contests/arc103/tasks/arc103_a

考察過程

1. 条件を言い換えると、偶数番目と奇数番目をそれぞれ同じ数にしたい
2. ただし、数列に現れる数はちょうど2種類なので、全て同じ数というのはダメ(コーナーケースになりそう)
3. 偶数番目を同じ数にすることを考えると、一番良く出てくる数にするのが最適
4. この最適戦略を偶数番目と奇数番目で行うとすべて同じ数になる可能性がある
5. 同じ数になる場合は、どちらかを妥協して2番目によく出てくる数にする
6. どちらを妥協するかはどちらとも試せば良さそう

解法

https://beta.atcoder.jp/contests/arc103/submissions/3304236

方針としては単純で、一番良く出てくるにするのが最適方針である。
答えはN-(偶数番目で一番良く出て来る数の個数 + 奇数番目で一番良く出てくる数の個数)となる。
個数の和を最大化する問題として考えていこう。
 
まずは、偶数番目・奇数番目でそれぞれ出てくる数の頻度を数えよう。
奇数番目を0, 偶数番目を1とする。
cnt[i][j] := i番目で数jが出てくる回数
これを使って、「v[i] := i番目で{出てくる回数, 数j}として降順ソートしたもの」をつくる
 
これがあれば、基本はv[0][0].first+v[1][0].firstが最適。
しかし、v[0][0].second = v[1][0].secondの場合は、すべて同じ数になってしまうのでダメ。
その場合は、どちらかを妥協することを考えよう。
 
どちらも妥協できない場合は、全て同じ数だった場合なので、片方をすべて違う数に変えるので、N/2が答え。
どちらかを妥協するが、どっちを妥協するのが最適かはどちらも試してより良い方を採用しよう。

int N, V[101010];
//---------------------------------------------------------------------------------------------------
void _main() {
    cin >> N;
    rep(i, 0, N) cin >> V[i];
 
    map<int, int> cnt[2];
    rep(i, 0, N) cnt[i % 2][V[i]]++;
 
    vector<pair<int, int>> v[2];
    rep(i, 0, 2) {
        fore(p, cnt[i]) v[i].push_back({ p.second, p.first });
        sort(all(v[i]), greater<pair<int,int>>());
    }
 
    // どちらも最適
    if (v[0][0].second != v[1][0].second) {
        int ans = N - v[0][0].first - v[1][0].first;
        cout << ans << endl;
        return;
    }
 
    // すべて同じ数だった
    if (v[0].size() == 1 and v[1].size() == 1) {
        int ans = N / 2;
        cout << ans << endl;
        return;
    }
 
    int ans = inf;
    if (2 <= v[0].size()) chmin(ans, N - v[0][1].first - v[1][0].first);
    if (2 <= v[1].size()) chmin(ans, N - v[0][0].first - v[1][1].first);
    cout << ans << endl;
}