アルゴリズムの問題にチャレンジした

12月です。いろんなAdventCalendarがあって、楽しませてもらってます。今回はその中でも、id:hama_DUさんの記事 http://d.hatena.ne.jp/hama_DU/20111204 の問題がRTで流れてきて、興味を持ったので、勝手に挑戦してみようと思います(迷惑だったら言ってください、消します)。べ、べつに他に書くネタが無いからってわけじゃないよ?

できた

最初にできたコード

#include <iostream>
#include <vector>
#include <algorithm>
#include <array>

int main()
{
	int input = -1;

	const int d_min = 1;
	const int d_max = 25;

	while( input != 0 )
	{
		std::cin >> input;

		std::array< int, d_max > array = {};

		if( input != 0 )
		{
			std::vector< int > vect;

			for( int i = 0; i < input; ++i )
			{
				int num = 0;
				std::cin >> num;
				vect.push_back( num );
			}

			std::sort( vect.begin(), vect.end() );

			int max = vect.back();

			{
				const int max_count = std::count( vect.begin(), vect.end(), max );

				for( int i = 0; i < max_count; ++i )
				{
					auto it = std::min_element( & array[ max - 1 ], & array[ d_max - 1 ] + 1 );
					++( * it );
				}
			}

			for( auto it = vect.rbegin(), end = vect.rend(); it != end; ++it )
			{
				if( * it < max )
				{
					max = * it;

					const int max_count = std::count( vect.begin(), vect.end(), max );

					for( int j = 0; j < max_count; ++j )
					{
						auto it = std::min_element( & array[ max - 1 ], & array.back() + 1 );
						++( * it );
					}
				}
			}
			std::cout << * std::max_element( array.begin(), array.end() ) << std::endl;
		}

	}
	return 0;
}

大体30分位( + std::arrayの仕様(&array[ size ] のassert )に悩まされること長し)。とても遅い(アルゴリズム的な意味でも)。
毎回全探索していますね。ソートして、どんな日でもいい人ではなく、一番条件の厳しい人から順番に、"条件内で一番空いている日"を毎回探しています。

改良した

あまりに効率の悪いコードです。もうちょっと効率よくできそうです。

#include <iostream>
#include <vector>
#include <algorithm>
#include <array>

int main()
{
	int input = -1;

	const int d_min = 1;
	const int d_max = 25;

	while( input != 0 )
	{
		std::cin >> input;

		std::array< int, d_max > array = {};

		if( input != 0 )
		{
			std::vector< int > vect;

			for( int i = 0; i < input; ++i )
			{
				int num = 0;
				std::cin >> num;
				vect.push_back( num );
			}

			std::sort( vect.begin(), vect.end() );

			for( auto it = vect.rbegin(), end = vect.rend(); it != end; ++it )
			{
				bool find = false;
				for( std::size_t i = *it - 1, size = array.size(); i < size - 1; ++i )
				{
					if( array[ i ] < array [ i + 1 ] )
					{
						++array[ i ];
						find = true;
						break;
					}
				}

				if( ! find )
					++array.back();
			}
			std::cout << * std::max_element( array.begin(), array.end() ) << std::endl;
		}

	}
	return 0;
}

10行ほど短くなりましたね。こっちのほうが速いです。条件内で一番後ろから、可能な限り公平に分散できるようにしました。条件内で、全ての日が同じ人数で埋まっていれば最後の日に、そうでない場合は、奥から順に埋まっていくので、手前の日から見て行って、次の日よりも人数が少ない日に人を配置します(もちろん、条件内で)。

最後に

特にまとめも思い浮かびませんが、解いていて楽しかったです。後は、改良したコードをすぐに思いつけるようになりたいですね!