公式

F - Socks 2 解説 by yuto1115

解説

まず、なくしていない色の靴下はその色同士で組を作るのが最適です。

証明

あるなくしていない色 $p$ に対し、色 $p$ の靴下 $2$ 枚が組を作っていない場合を考えます。それぞれ他の色 $A_i, A_j$ の靴下と組になっているとき、三角不等式より $|A_i-A_p| + |A_j-A_p| \geq |A_i-A_j| = |A_i-A_j| + |A_p-A_p|$ ですから、$(A_p,A_i),(A_p,A_j)$ の $2$ 組を作る代わりに $(A_p,A_p),(A_i,A_j)$ の $2$ 組を作っても奇妙さの総和は増加しません。色 $p$ の靴下のうち $1$ 枚がどの組にも含まれず、もう $1$ 枚の靴下が他の色 $A_i$ の靴下と組になっているとき、$(A_p, A_i)$ を作って $A_p$ を $1$ 枚余らせる代わりに、$(A_p, A_p)$ を作って $A_i$ を $1$ 枚余らせても奇妙さの総和は増加しません。よって、最適解においては色 $A_p$ の靴下 $2$ 枚が組を作っているとしてしまって問題ありません。


よって、色 \(A_1,A_2,\dots,A_K\) の靴下 \(1\) 枚ずつから \(\lfloor \frac{K}{2} \rfloor\) 組を作る問題と考えることができます。

\(K\) が偶数のとき、\((A_1,A_2),(A_3,A_4),\dots,(A_{K-1},A_K)\) というように(昇順に並べた列内で)隣接する色同士をペアにしていくのが直感的にも最適そうですが、実際に最適です。

証明

$K$ に関する帰納法で示すことを考えると、最適解において色 $A_1$ と組になるのが常に色 $A_2$ であることを示せば十分です。

上記の内容を背理法で示します。最適解において、色 $A_1$ と組になるのが色 $A_i\ (i > 2)$ であり、代わりに色 $A_2$ と組になるのが色 $A_j$ であったとします。このとき、$A_1 < A_2 < A_i, A_j$ より$|A_i-A_1| + |A_j-A_2| > |A_2-A_1| + |A_j-A_i|$ ですから、$(A_1,A_i),(A_2,A_j)$ の $2$ 組を作るより$(A_1,A_2),(A_i,A_j)$ の $2$ 組を作った方が奇妙さの総和が小さいことになり、最適性に矛盾します。


問題は \(K\) が奇数の時ですが、この場合はどの組にも含まれない唯一の色を全探索すれば、残る靴下から組を作る方法は \(K\) が偶数のときと同様になります。どの組にも含まれない唯一の色を固定したとき、残る靴下をうまく組にしたときの奇妙さの総和の最小値は単純に求めると \(O(N)\) かかり、全体で \(O(N^2)\) になってしまいます。そこで、\(\mathrm{presum}[2]=(A_2-A_1),\mathrm{presum}[4]= (A_4-A_3)+(A_2-A_1), \mathrm{presum}[6]=(A_6-A_5)+(A_4-A_3)+(A_2-A_1),\dots\) のように、先頭から順に組にして行ったときの奇妙さの総和を累積和として求め、末尾からの累積和も同様に求めておくことで、全体で \(O(N)\) で本問題を解くことができます。

おまけ:\(K\) が奇数のとき、どの組にも含まれない唯一の色は全て試してももちろん良いですが、\(A_1,A_3,A_5,\dots,A_K\) だけを探索しても良いです(証明は省略)。下記の実装例では、実装の簡略化のためこの事実を用いています。

実装例 (C++) :

#include<bits/stdc++.h>

using namespace std;

int main() {
    int n, k;
    cin >> n >> k;
    vector<int> a(k);
    for (int &i: a) cin >> i;
    vector<int> pre(k + 1), suf(k + 1);
    for (int i = 1; i <= k; i++) {
        pre[i] = pre[i - 1];
        if (i % 2 == 0) pre[i] += a[i - 1] - a[i - 2];
    }
    for (int i = k - 1; i >= 0; i--) {
        suf[i] = suf[i + 1];
        if ((k - i) % 2 == 0) suf[i] += a[i + 1] - a[i];
    }
    int ans = 1e9;
    for (int i = 0; i <= k; i += 2) {
        ans = min(ans, pre[i] + suf[i]);
    }
    cout << ans << endl;
}

投稿日時:
最終更新: