AOJ 2425: 全探索お姉さんの休日

解法

x, y, t mod 6によって方向は定まるので、これらの(x,y,t%6)を頂点とする有向グラフを作り、ダイクストラする。

コード

import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Scanner;

public class Main {
	private final int[] DX = { 0, 1, 1, 0, -1, -1 };
	private final int[] EDY = { 1, 0, -1, -1, -1, 0 };// x座標が偶数の時のyの変化量
	private final int[] ODY = { 1, 1, 0, -1, 0, 1 };// x座標が奇数の時のyの変化量
	private final int MAX = 105;
	private final int WIDTH = MAX * 2 + 1;

	public void solve() {
		Scanner scanner = new Scanner(System.in);
		int[] s = new int[2];
		int[] g = new int[2];
		for (int i = 0; i < 2; i++) {
			s[i] = scanner.nextInt();
		}
		for (int i = 0; i < 2; i++) {
			g[i] = scanner.nextInt();
		}

		int n = scanner.nextInt();
		boolean[][] unreachable = new boolean[WIDTH][WIDTH];
		for (int i = 0; i < n; i++) {
			int x = scanner.nextInt() + MAX;
			int y = scanner.nextInt() + MAX;
			unreachable[x][y] = true;
		}

		int limx = scanner.nextInt();
		int limy = scanner.nextInt();
		scanner.close();

		int N = WIDTH * WIDTH * 6;
		Graph graph = new Graph(N);

		for (int x = -limx; x <= limx; x++) {
			for (int y = -limy; y <= limy; y++) {
				if (unreachable[x + MAX][y + MAX]) {
					continue;// 家具がある場所は調べない
				}
				for (int t = 0; t < 6; t++) {
					int from = getVertex(x, y, t);
					int d = Math.abs(x * y * t) % 6;// 指示された方向

					for (int dir = 0; dir < 6; dir++) {
						// 方向dirで隣接するマス
						int nx = x + DX[dir];
						int ny = y + EDY[dir];
						if (Math.abs(x) % 2 != 0) {
							ny = y + ODY[dir];
						}

						// 領域外には移動できない
						if (Math.abs(nx) > limx || Math.abs(ny) > limy) {
							continue;
						}

						// 家具があったら移動できない
						if (unreachable[nx + MAX][ny + MAX]) {
							continue;
						}

						int to = getVertex(nx, ny, t + 1);
						if (dir == d) {
							graph.addEdge(from, to, 0);// 指示通りならコスト0
						} else {
							graph.addEdge(from, to, 1);// 指示を無視する時はコスト1
						}
					}
					int to = getVertex(x, y, t + 1);
					graph.addEdge(from, to, 1);// 留まる場合はコスト1
				}
			}
		}

		int start = getVertex(s[0], s[1], 0);
		long[] minDists = graph.minDistDijkstra(start);
		long ans = Integer.MAX_VALUE;

		// 各tについて最小のコストを調べる
		for (int t = 0; t < 6; t++) {
			int goal = getVertex(g[0], g[1], t);
			ans = Math.min(ans, minDists[goal]);
		}
		if (ans >= Integer.MAX_VALUE) {
			System.out.println(-1);
			return;
		}
		System.out.println(ans);
	}

	private int getVertex(int x, int y, int t) {
		// 座標を1つにする
		int nx = x + MAX;
		int ny = y + MAX;
		t %= 6;
		return t * WIDTH * WIDTH + nx * WIDTH + ny;
	}

	public static void main(String[] args) {
		new Main().solve();
	}
}

class Graph {
	public static final long INF = Long.MAX_VALUE / 2;
	int n;
	ArrayList<Edge>[] graph;

	@SuppressWarnings("unchecked")
	public Graph(int n) {
		this.n = n;
		this.graph = new ArrayList[n];
		for (int i = 0; i < n; i++) {
			graph[i] = new ArrayList<Edge>();
		}
	}

	public void addBidirectionalEdge(int from, int to, int cost) {
		addEdge(from, to, cost);
		addEdge(to, from, cost);
	}

	public void addEdge(int from, int to, int cost) {
		graph[from].add(new Edge(to, cost));
	}

	// dijkstra O(ElogV)
	public long[] minDistDijkstra(int start) {
		long[] dist = new long[n];
		Arrays.fill(dist, INF);
		dist[start] = 0;

		PriorityQueue<Node> priorityQueue = new PriorityQueue<Node>();
		priorityQueue.offer(new Node(0, start));

		while (!priorityQueue.isEmpty()) {
			// キューから1番距離の近いノードを取り出す
			Node node = priorityQueue.poll();
			int v = node.id;
			if (dist[v] < node.dist) {
				// 暫定の最短距離よりも遠かったらスルー
				continue;
			}

			for (Edge e : graph[v]) {
				// 取り出したノードから出ている全ての辺について調べ、
				// 暫定の最短距離が更新される場合は更新してキューに入れる
				if (dist[e.to] > dist[v] + e.cost) {
					dist[e.to] = dist[v] + e.cost;
					priorityQueue.add(new Node(dist[e.to], e.to));
				}
			}
		}
		return dist;
	}

}

class Edge {
	int to;
	int cost;

	public Edge(int to, int cost) {
		this.to = to;
		this.cost = cost;
	}
}

class Node implements Comparable<Node> {
	long dist;
	int id;

	public Node(long dist, int i) {
		this.dist = dist;
		this.id = i;
	}

	public int compareTo(Node o) {
		if (this.dist > o.dist) {
			return 1;
		} else if (this.dist < o.dist) {
			return -1;
		} else {
			return 0;
		}
	}
}