きちんとした手続きを経て求めたいところ
問題概要
個の整数からなる数列 が与えられる。この数列に次の操作を繰り返して、数列の項がすべて相異なるようにしたい。できあがる数列の項数の最大値を求めよ。
【操作】
数列から 3 個の整数を選ぶ。それら 3 個の整数の最小値と最大値を削除して 1 個だけ残す。
制約
- は奇数
考えたこと
問題の意味を掴むのが大変かもしれない。このようなときは、いろいろ手を動かして実験してみよう。たとえば
- (3, 3, 3, 3, 3) のように同じ数が 3 個以上あるとき、そこから 3 個を選ぶと、 個 → 個に減らすことができる
- (2, 2, 3, 3) のように同じ数が 2 個ずつあるとき:そこからどのように 3 個を選んでも、2 個ずつ → 1 個ずつに減らすことができる
といったことが見て取れる。いずれにしても「重複度」的な数量が 2 だけ減少するように思える。
数列の「重複度」を定義する
上の「重複度」という数量を次のように定義しよう。
各整数値 に対して、数列中に が 個だけあるとき、 の値を足していく。
この値を数列の重複度とよぶことにする。
数列の値がすべて相異なるとき、重複度は 0 である。われわれの目標は、数列の重複度を減らしていき、最終的に 0 にすることである。
ここで、次のことに着目しよう。
- どのように操作をしても、重複度が 2 以上減ることはない(そもそも数列から 2 個より多くの数を減らさないため)
- 重複度が 2 以上であるとき、重複度を 2 だけ減らすことができる(重複しているところを狙い撃つようにする)
- 重複度が 1 であるとき、重複度を 0 にすることができる
これらから、問題は次のようにシンプルなものになる。
「重複度を 2 ずつ減らしていったときに、何回で 0 にできるか」
ここまで来れば簡単な算数である。重複度を求めるのは、ソートや map
でできる。計算量は となる。
コード
#include <bits/stdc++.h> using namespace std; int main() { int N; cin >> N; vector<int> A(N); for (int i = 0; i < N; i++) cin >> A[i]; sort(A.begin(), A.end()); int res = 0; for (int i = 0; i+1 < N; i++) if (A[i] == A[i+1]) res++; cout << N - (res + 1) / 2 * 2 << endl; }