SRM 652 Div. 2 Hard: NoRightTurnDiv2

解法

点0から始めて、曲がり角が最も大きくなるように貪欲に点を取っていくと上手くいく。

コード

public class NoRightTurnDiv2 {
	double[] direction(int[] X, int[] Y, int from, int to) {
		// fromからtoへの単位ベクトルを返す
		double dx = X[to] - X[from];
		double dy = Y[to] - Y[from];
		return new double[] { dx / Math.sqrt(dx * dx + dy * dy), dy / Math.sqrt(dx * dx + dy * dy) };
	}

	double product(double[] a, double[] b) {
		// ベクトル内積を返す
		return a[0] * b[0] + a[1] * b[1];
	}

	public int[] findPath(int[] x, int[] y) {
		int start = 0;
		int N = x.length;

		// x座標のもっとも大きい点を始点にする
		for (int i = 1; i < N; i++) {
			if (x[start] < x[i])
				start = i;
		}

		int[] ans = new int[N];
		ans[0] = start;

		boolean[] used = new boolean[N];
		used[start] = true;

		// Y軸の正の向きの単位ベクトル
		double[] prevDir = new double[2];
		prevDir[0] = 0.0;
		prevDir[1] = 1.0;

		for (int i = 1; i < N; i++) {
			int from = ans[i - 1];
			double max = -2;
			int next = -1;

			for (int to = 0; to < N; to++) {
				if (!used[to]) {
					// 前のベクトルに対して最も内積が大きくなるように次の点を探す
					// そうすると、曲がり角を最大にすることが出来る
					double[] toDir = direction(x, y, from, to);
					double pro = product(prevDir, toDir);
					if (max < pro) {
						max = pro;
						next = to;
					}
				}
			}
			prevDir = direction(x, y, from, next);
			ans[i] = next;
			used[next] = true;
		}
		return ans;
	}
}