白駿道路建設(14276)

16233 ワード

道路をつくる
1.ヒント
1)道路の建設順序と道路の方向を押し付けてみる
2)必要な遷移情報は配置可能な道路接続の状態である.
3)XOR演算を用いて接続する道路の個数を表すことができる.
2.近接
問題を分解できますか?
私たちは問題によって与えられた条件で道路を建設する数量を求めなければならない.最も直感的な方法はNNNの都市に道路を建設する場合,道路の数を分けて自ら試み,創造した結果が条件を満たす場合にのみ計算すればよい.もちろん、状況の数をはっきりさせ、何も繰り返さず、含まない.
ケースの数特性により参照の透明性が満たされるのでDPで解決できる.
順序を強制できますか?
道路建設の順序は問題の答えとは関係ない.そこで,状況数を求める手順を番号の小さい順に簡単に区分する.
この場合,与えられたNNN都市を[i,N]に分割するのが最善の方法である. (0≤i どのような形式の点火式かは不明であるが、D[i]D[i]D[i][i]は、D[i+1]D[i+1]D[i+1]を再帰的に呼び出す形式である.
また、iii都市ごとに2つの選択肢しかありません.道路を接続するか、接続しないかです.道路は都市標準の左側にも右側にも接続できるようになりました.道路は自由ですが、強制方向は右側に建ててみてください.道路に方向性がないため、状況の数は繰り返されない.
この順序に従って、存在する可能性のある全ての道路を道路の始点と道路の長さw(ここでは、 length)w(here,\length)w(here, lengthで表すことができます.もし私たちが道を小さい頃から大きくしようとしたら、私たちができることは2つの選択しかありません.
1. w(here, length)w(here,\length)w(here, lengthは書かず、次へ.
2. w(here, length)w(here,\length)w(here, lengthと書いて、自分を呼び直します.
しかし問題は、全ての都市の隣接道路の個数が偶数でなければならず、w(here,K)w(here,K)w(here,K)w(here,K)w(here,K)道路からw(here+1,1)w(here+1,1)w(here+1,1)に移行すると、ここを過ぎると、二度とここで道路を隣接させることができないことがわかります.では、ここは偶数の道路に隣接しなければなりません.
3.実施
1)動的プログラミング
N, K, MN,\K,\MN, K, Mがある場合、状況の数は常に同じです.したがって、DPを用いて重複計算を最大限に解消することができる.
2)ビットフィールドによる動的プログラミング
ここで道路の建設を開始すると、道路の反対側の都市でも隣接する道路の数を維持し、再呼び出し時に以前の情報に渡すことができるので、ビットタグを使用してフィールドを維持します.偶数+1は奇数,奇数+1は偶数であり,XOR演算により実現がより簡単である.
3)時間複雑度
存在する可能性のある最大ローカル問題数
O(N×(K+1)×(M+1)×2K+1)O(N\times (K + 1)\times (M + 1)\times 2^{K + 1})O(N×(K+1)×(M+1)×2 K+1),1関数の時間的複雑度はO(1)であった.
O(N×(K+1)×(M+1)×2K+1)O(N\times (K + 1)\times (M + 1)\times 2^{K + 1})O(N×(K+1)×(M+1)×2 K+1)となります.
4)テストケース
1 0 1
->1
간선을 하나도 놓지 않는 경우 한 개 밖에 없다.
30 30 8
->201860393
가장 큰 입력.
12 23 4
->62805419
아무거나 입력해 봤다.
public class Main {
	static int N, K;
	static int[][][][] cache;
	
	static final int MOD = 1000000007;
	
	// start번째 도시에서 length길이 이상의 도로를 m개 건설하려고 할 때,
	// [start, start + K]의 상태가 b일때 경우의 수
	static int dp(int start, int length, int m, int b) {
		// 맨 마지막 도시인 경우 더 이상 지을 간선이 없고, b에 나타난 도시들의 연결된 간선들의 개수가 모두 짝수여야 경우를 하나 찾음
		// 더 이상 지을 간선이 없는 경우, b에 나타난 도시들의 연결된 간선들의 개수가 모두 짝수여야 경우를 하나 찾음
		if (start >= N - 1 || m == 0) return m == 0 && b == 0 ? 1 : 0;
		// 간선의 길이가 K를 넘었거나, 범위를 벗어나면 다음 도시에 대해서 시도
		if (length > K || start + length >= N) return (b & 1) == 0 ? dp(start + 1, 1, m, b >> 1) : 0;
		if (cache[start][length][m][b] != -1) return cache[start][length][m][b];
		// length길이의 간선을 짓지 않는 경우
		int sum = dp(start, length + 1, m, b);
		// length길이의 간선을 하나 더 짓는 경우
		sum = (sum + dp(start, length, m - 1, b ^ 1 ^ (1 << length))) % MOD;
		return cache[start][length][m][b] = sum;
	}
	
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine(), " ");
		N = Integer.parseInt(st.nextToken());
		int M = Integer.parseInt(st.nextToken());
		K = Integer.parseInt(st.nextToken());
		cache = new int[N][K + 1][M + 1][1 << (K + 1)];
		for (int i = 0; i < cache.length; i++)
			for (int j = 0; j < cache[i].length; j++)
				for (int k = 0; k < cache[i][j].length; k++)
					Arrays.fill(cache[i][j][k], -1);
		System.out.println(dp(0, 1, M, 0));
	}

}