1. 程式人生 > 實用技巧 >【狀壓dp】Y

【狀壓dp】Y

題意

一個圖中,每條邊的邊權是0或1,求出長度為d的不同的路徑數(路徑即經過邊的01串)。

思路

考慮折半,最後把d分成2部分的01串組合起來。
設dp[i]為狀態為i時的終點點集,f[i]為狀態為i時的起點點集(2個bitset)。
列舉2段01串,判斷是否存在中間點可以轉移,累加答案。

程式碼

#include <cstdio>
#include <bitset>

const int MAXS = 1 << 11;
std::bitset<91> dp[MAXS], f[MAXS], g0[91], g1[91];
int n, m, d, d1, d2, ans;

int main() {
	scanf("%d %d %d", &n, &m, &d);
	for (int i = 1, u, v, c; i <= m; i++) {
		scanf("%d %d %d", &u, &v, &c);
		c ? g1[u][v] = g1[v][u] = 1 : g0[u][v] = g0[v][u] = 1;
	}
	d2 = d >> 1;
	d1 = d - d2;
	for (int u = n; u; u--) {
		for (int i = 0; i < MAXS; i++)
			dp[i].reset();
		dp[1][u] = 1;//在前面放1表示長度為多少的狀態
		for (int i = 1; i < 1 << d1; i++)
			for (int j = 1; j <= n; j++)
				if (dp[i][j]) {
					dp[i << 1] |= g0[j];
					dp[i << 1 | 1] |= g1[j];
				}
		for (int i = 0; i < 1 << d1; i++)
			f[i][u] = dp[1 << d1 | i].any();
	}
	for (int i = 0; i < 1 << d1; i++)
		for (int j = 0; j < 1 << d2; j++)
			if ((dp[1 << d2 | j] & f[i]).any()) ans++;
	printf("%d", ans);
}