1. 程式人生 > 實用技巧 >CodeForces - 111C(狀壓dp)

CodeForces - 111C(狀壓dp)

int n, m;
int dp[21][1 << 7][1 << 7];//dp[row][now][pre]
int num[1 << 7];//存該狀態有幾個1

inline int lowbit(int x) {
	return (x & (-x));
}

//注意n * m < 40
//所以min(n, m) <= 6,swap即可
int main() {
	scanf("%d %d", &n, &m);
	if (n < m)	swap(n, m);

	if (m == 1) {
		printf("%d\n", n / 3 * 2 + (n % 3 == 2 ? 1 : 0));
		return 0;
	}
	//if (m == 2) {
	//	printf("%d\n", n / 3 * 4 + n % 3);
	//	return 0;
	//}

	//if (n == 3 || m == 3) {

	//}

	//保證m 小於 7
	int limit = 1 << m;
	for (int i = 1; i < limit; ++i)
		num[i] = num[i - lowbit(i)] + 1;

	for (int i = 0; i < limit; ++i) {
		dp[0][i][limit - 1] = m - num[i];//1表示當前位置有蜘蛛
	}

	for (int now = 0; now < limit; ++now) {
		for (int pre = 0; pre < limit; ++pre) {
			int temp = pre << 1;//不能超出limit-1
			if (temp >= limit)	temp -= limit;
			if ((pre | temp | (pre >> 1) | now) != limit - 1)	continue;
			dp[1][now][pre] = max(dp[1][now][pre], dp[0][pre][limit - 1] + m - num[now]);
		}
	}

	for (int i = 2; i < n; ++i) {//當前行
		for (int now = 0; now < limit; ++now) {//當前的狀態
			for (int pre = 0; pre < limit; ++pre) {//上一次的狀態
				//if ((pre | (now << 1) | (now >> 1) | now) != limit - 1)	continue;
				for (int prep = 0; prep < limit; ++prep) {
					int temp = pre << 1;//不能超出limit-1
					if (temp >= limit)	temp -= limit;
					if ((pre | temp | (pre >> 1) | now | prep) != limit - 1)	continue;
					dp[i][now][pre] = max(dp[i][now][pre], dp[i - 1][pre][prep] + m - num[now]);
				}
			}
		}
	}

	int ans = 0;
	for (int now = 0; now < limit; ++now) {
		for (int pre = 0; pre < limit; ++pre) {
			int temp = now << 1;
			if (temp >= limit)	temp -= limit;
			if ((pre | temp | (now >> 1) | now) != limit - 1)	continue;
			ans = max(ans, dp[n - 1][now][pre]);
			//printf("dp[%d][%d][%d] = %d%c", n - 1, now, pre, dp[n - 1][now][pre], pre == limit - 1 ? '\n' : ' ');
		}
	}
	printf("%d\n", ans);

	return 0;
}