「第9回 オフラインリアルタイムどう書く」へなちょこ解答(Java)

オフラインリアルタイムどう書くとは
鍋谷さんが出してくださるお題を、参加者が好きなプログラミング言語で解いて楽しむ会です。

春の嵐が来る中、オフラインリアルタイムどう書くが強行されました。

今回は、バス代です!

「今までで一番カンタンだった」という方もいる中、自分は時間内に解けませんでした。6つくらいテストケースが通らなかった……(定期券を持つ幼児を考慮し忘れていたのが敗因)。2連敗なので次は何とかしたいところです。

今回は、オフラインリアルタイムどう書く史上初めて、参加者の言語がすべてバラバラでした。主催の鍋谷さんを含めて7人参加で、C++HaskellJavaObjective-CPerlPythonRuby(アルファベット順)が使用されました。発表も面白かったです。

自分の解答(Java

(また帰りの電車の中でデバッグする羽目に)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 問題: http://nabetani.sakura.ne.jp/hena/ord9busfare/
 */
public class Busfee {
	private final boolean isAdult(final String string) {
		return string.charAt(0) == 'A';
	}

	private final boolean isInfant(final String string) {
		return string.charAt(0) == 'I';
	}

	private final boolean hasPass(final String string) {
		return string.charAt(1) == 'p';
	}

	private final boolean hasWelfare(final String string) {
		return string.charAt(1) == 'w';
	}

	public String solve(final String input) {
		int base = Integer.valueOf(input.split(":")[0]);
		String passenger = input.split(":")[1];
		List<String> passengers = Arrays.asList(passenger.split(","));

		int adultNum = countAdult(passengers);
		int result = 0;

		int freeNum = 0;
		List<String> welfareInfants = new ArrayList<>();
		for (int i = 0; i < passengers.size(); i++) {
			String one = passengers.get(i);
			if (isInfant(one) && freeNum < adultNum * 2) {
				if (hasPass(one)) {
					continue;
				}
				if (hasWelfare(one)) {
					welfareInfants.add(one);
					continue;
				}
				freeNum++;
				continue;
			}
			result += getFee(one, base);
		}

		for (String each : welfareInfants) {
			if (freeNum < adultNum * 2) {
				freeNum++;
				continue;
			}
			result += getFee(each, base);
		}
		return String.valueOf(result);
	}

	private int getFee(final String target, final int base) {
		int tmp = 0;
		if (isAdult(target)) {
			tmp = base;
		} else {
			tmp = getHalf(base);
		}
		return discount(target, tmp);
	}

	private int discount(final String target, final int original) {
		if (hasPass(target)) {
			return 0;
		} else if (hasWelfare(target)) {
			return getHalf(original);
		}
		return original;
	}

	private int getHalf(final int n) {
		int tmp = n / 2;
		if (tmp % 10 == 0) {
			return tmp;
		}
		return tmp + (10 - tmp % 10);
	}

	private int countAdult(final List<String> passengers) {
		int result = 0;
		for (String each : passengers) {
			if (isAdult(each)) {
				result++;
			}
		}
		return result;
	}
}

考え方

乗客が幼児であった場合、無料枠(大人の人数×2)に収まる人数分は、無料になります。

その際、福祉割引が適用される幼児(通常の幼児料金のさらに半額、つまり、大人の4分の1の額になる)よりも、福祉割引が適用されない幼児を優先して無料枠に入れてあげた方が、全体の料金は安くなります。

というわけで、福祉割引が適用される幼児がいた場合は、無料適用を後回しにします。

修正点メモ

会場で書いたコードからどこを直したか。

  1. 「半額にする、ただし端数は切り上げる」の実装を間違えていたので直した
    • ついでに、メソッドに切り出した
  2. 不要な条件分岐をなくした
    • 幼児が0、または大人が0のとき・・・などで処理を分けるのをやめた
  3. ADULT == 'A'、INFANT == 'I'等の定数定義をやめて、メソッドにした
  4. i番目の乗客の年齢区分をageArray[i]に、割引種別をkindArray[i]に格納していたが、Listに乗客をそのまま入れることにした

1と2は、発表時に指摘いただきました。

3と4は必須の修正ではありませんでしたが、たとえば、if文の条件式で乗客が幼児かどうかを判定するとき、「リスト要素i番目の乗客の1文字目は'I'か」などと書くよりも、「isInfant(one)」とした方が何をしたいかが分かりやすい気がしたので、変更しました。

そんな感じです。

今回も楽しかったです。ありがとうございました。