poj - 1185(状压dp)

#pragma warning (disable : 4996)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;

const int maxn = 1e6 + 10;
char s[110][11];
int dp[110][80][80];///第i行,第i行取的状态是j,第i - 1行取得状态是k
int tot[maxn];
int num[maxn];
int op[110];//预处理,存储该行的山坡状态
//~9 = -10(因为负数是补码存储的)
//~9 = ~[00001001]原 = [11110110]补 = [11110101]反 = [10001010]原 = -10
//要求一个负数的二进制表示方式,可以先求出它的原码(即负数的绝对值的二进制位),然后每位取反得出它的反码,反码再加1得出补码
//所以实际补码就是源码的最后一个1及以后的位数不变,前面的全取反
//所以lowbit(i) 就是i最后一个1及以后的二进制的值
inline int lowbit(int x) {
	return (x & (-x));
}

int main() {
	int n, m, cnt = 0, ans = 0;
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; ++i)	scanf("%s", s[i]);
	for (int i = 0; i < (1 << m); ++i)	if (((i & (i >> 1)) == 0) && ((i & (i >> 2)) == 0))	tot[cnt++] = i;
	//cout << cnt;	m = 10, cnt = 60//最多有60种状态满足条件
	for (int i = 1; i < (1 << m); ++i)	num[i] = num[i - lowbit(i)] + 1;//预处理出当前状态有几个1
	//lowbit(i) 即2 ^ i的二进制下末尾0的个数  //lowbit(3) = 1, lowbit(2) = 2;
	//通过动态计算统计每种状态包含1的个数
	for (int i = 1; i <= n; ++i) {
		for (int j = 0; j < m; ++j)	op[i] |= ((s[i][j] == 'H') << j);
		for (int j = 0; j < cnt; ++j) {///枚举第i层的状态
			if (tot[j] & op[i])	continue;
			for (int k = 0; k < cnt; ++k) {///枚举第i - 1的状态
				if (tot[j] & tot[k])	continue;
				if (i >= 2 && (op[i - 1] & tot[k]))	continue;
				int val = num[tot[j]];
				for (int z = 0; z < cnt; ++z) {///枚举第i - 2的状态
					if ((tot[z] & tot[j]) || (tot[z] & tot[k]))	continue;
					if (op[i - 2] & tot[z])	continue;
					dp[i][j][k] = max(dp[i][j][k], dp[i - 1][k][z] + val);
				}
				ans = max(ans, dp[i][j][k]);
			}
		}
	}
	printf("%d\n", ans);
}
posted @ 2020-09-29 22:54  wansheking  阅读(77)  评论(0)    收藏  举报