torus711 のアレ

主に競技プログラミングの問題について書きます

TopCoder, Single Round Match 647, Division 2, Level 1 : PeacefulLine

問題概要

 何人かの生徒を直線状に並べたい.生徒を並べたとき,隣り合う生徒の年齢が同じ場合,お喋りを初めてしまって迷惑になることが知られている.
 今,並べたい生徒たちの年齢に関する情報が,配列 x により与えられる.x_ii 番の生徒の年齢を表す.
 隣り合う生徒のペア全てについて,2 人の年齢が異なるような並べ方は存在するか?

解法

 |x| = N と書きます.また,問題設定からは離れて,列の並べ替えであって,隣り合う要素が全て相異なるようなものを探す問題として書きます.
 そのような並べ替えを考えたとき,ボトルネックになりそうなのは列に多く含まれる値だろうという予想が付きます.そこで,x に最も多く含まれる要素に着目して,そのような要素の数を m と置きます.最も多く含まれる値の配置はいくつか考えられますが,同じ長さの列に対してできるだけ多く詰め込んでしまいたいので,1 個おきに(奇数番目のみ or 偶数番目のみ)配置していく方がよさそうです.N が奇数のとき,偶数番目の要素は奇数番目のそれより数が少ないので,最も多く含まれる値は,奇数番目に配置するのが最適です.そう考えると,奇数番目の要素の数が m 以上ならば条件を満たす並べ替えが存在することになります.すなわち,m \leq \frac{ N + 1 }{ 2 } または辺々 2 倍して 2m \leq N + 1 ならば可能です.

コード

using VI = vector< int >;

#define FOR( e, c ) for ( auto &e : c )
#define ALL( c ) ( c ).begin(), ( c ).end()

#define SZ( v ) ( (int)( v ).size() )

class PeacefulLine
{
public:
	string makeLine( vector <int> x )
	{
		VI counts( 25 );
		FOR( a, x )
		{
			++counts[ a - 1 ];
		}
		const int m = *max_element( ALL( counts ) );
		return m * 2 <= SZ( x ) + 1 ? "possible" : "impossible";
	}
};