Lilliput Steps

小さな一歩から着実に. 数学やプログラミングのことを書きます.

AOJ 2510 - Twin Book Report

問題文 : 双子の読書感想文


解法 :

場合分けを行って考える.

  • 最も本を読む時間が, その他の本を読む時間の総和より長い場合

この場合はアミが最も長い本を読んでいる間に, マミがそれ以外の全ての本を読み終わっているので, その間に生じた時間でいくつかの感想文をかける. 間に生じる時間はたかだか1000 であるから, $T \leqq 1000$ として$O(NT)$ 時間のDP で感想文を書ける時間の最大値を求めれば良い. 感想文を書けずに余った時間 + (本を読むのにかかる時間+感想を書くのにかかる時間) の総和 が求める答えである.

  • そのような本がない場合

この場合はアミが先に最も長い本から読み始め, その本を読み終わった段階で, マミが2 冊目(以降)の本を読んでいるので追っかけるように本を読むことで, (本を読むのにかかる時間+感想を書くのにかかる時間) の総和で全ての本が読める.

コード

#include <cstdio>
#include <vector>
#include <algorithm>

using namespace std;

int main()
{
	int N;
	
	while (scanf("%d", &N) && N){
		pair<int, int> a[1024];
		for (int i = 0; i < N; i++){
			scanf("%d %d", &a[i].first, &a[i].second);
		}
		
		sort(a, a + N);
		
		int sum = 0, ans = 0;
		for (int i = 0; i < N - 1; i++) sum += a[i].first;
		for (int i = 0; i < N; i++) ans += a[i].first + a[i].second;
		
		if (sum >= a[N - 1].first){
			printf("%d\n", ans);
		}
		else {
			bool dp[1001] = {false};
			dp[0] = true;
			int spend = 0;
			for (int i = 0; i < N - 1; i++){
				for (int j = a[N - 1].first - sum; j >= a[i].second; j--){
					dp[j] |= dp[j - a[i].second];
					if (dp[j]) spend = max(spend, j);
				}
			}
			printf("%d\n", ans + a[N - 1].first - sum - spend);
		}
	}
	
	return (0);
}