状态压缩dp

状态压缩dp

思想

主要是状态数量很多时,将状态转换成二进制数01010串,在将这些串在十进制中使用。

  • 特点:一般数据范围不会超过20。

没啥好说的,来看看题吧

例题

P2704 [NOI2001] 炮兵阵地
在这里插入图片描述
在这里插入图片描述
思路

  • 看到数据范围10*100不规则,还有个10,应该是状压(
  • 我们将每行的状态设置为0~(1<<10),表示是否在该点上放置守卫,由于每个炮兵能够攻击到上下2格的位置,那么炮兵能影响的是三行的位置。我们需要设置dp[n][cur][pre],表示当前到了第n行,同时该行的状态时cur,上一行的状态时pre,之后进行转移即可
  • 如何判断一行间两个炮兵距离大于2?可以判断(x & (x << 1)) || (x & (x << 2))即可
  • 设置dp[105][1<<10][1<<10]会MLE,学到了个新方法,设置滚动数组可以直接将那一维mod滚动的数量,比如这题可以直接开dp[3][1<<10][1<<10],之后每次数值都是%3,就刚好能够自动滚。
  • 详情看代码
#include <bits/stdc++.h>
using namespace std;
int n, m;
int g[105][11];
int G[105];
int dp[5][1 << 10][1 << 10];
int Sum[1 << 11];
bool judge(int x)
{
	if ((x & (x << 1)) == 0 && ((x & (x << 2)) == 0)){
		return 1;
	}	
	return 0;
}
void ini()
{
	for (int i = 0; i < (1 << m); i ++){	//初始化一个状态有多少个1
		int sum = 0;
		int t = i;
		while (t){
			int tt = t % 2;
			t /= 2;
			if (tt) sum ++;
		}
		Sum[i] = sum;
	}

	for (int i = 0; i < n; i ++){	//初始化每一行的山地平地数据
		int sum = 0;
		for (int j = 1; j <= m; j ++){
			sum *= 2;
            sum += g[i][j];	
            
		}
		G[i] = sum;
	}

	for (int i = 0; i < (1 << m); i ++){	//初始化第一行
		if ((i & G[0]) || !judge(i)) continue;
		dp[0][i][0] = Sum[i];
	}
	for (int i = 0; i < (1 << m); i ++){	//初始化第二行
		if ((i & G[1]) || !judge(i)) continue;
		for (int j = 0; j < (1 << m); j ++){
			if ((j & G[0]) || !judge(j)) continue;
			if (i & j) continue;
			dp[1][i][j] = Sum[i] + Sum[j];
		}
	}
}
int main()
{
	cin >> n >> m;
	for (int i = 0; i < n; i ++){
		for (int j = 1; j <= m; j ++){
			char c; cin >> c;
			if (c == 'H') g[i][j] = 1;
		}
	}
	ini();
	for (int i = 2; i < n; i ++){
		for (int j = 0; j < (1 << m); j ++){	//this
			if ((j & G[i]) || !judge(j)) continue;
			for (int k = 0; k < (1 << m); k ++){	//pre
				if ((k & G[i - 1]) || !judge(k)) continue;
				for (int p = 0; p < (1 << m); p ++){	//prepre
					if ((p & G[i - 2]) || !judge(p)) continue;
					if ((j & k) || (j & p) || (k & p)) continue;
					dp[i % 3][j][k] = max(dp[i % 3][j][k], dp[(i + 2) % 3][k][p] + Sum[j]);
				}
			}
		}
	}
	int maxx = 0; 
	for (int i = 0; i < (1 << m); i ++){
		for (int j = 0; j < (1 << m); j ++){
			maxx = max(maxx, dp[(n + 2) % 3][i][j]);
		}
	}
	cout << maxx << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值