poj 1185 状态压缩与预处理

本文介绍了一种使用状态压缩动态规划(状压DP)解决炮台布防问题的方法,通过预处理地图状态,利用三维数组存储最优解,避免了深度优先搜索(DFS)的超时风险,实现了高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前做这道题第一反应是用dfs,类似于八皇后问题,后来写到一半仔细想了想,无论怎么搜索,应该会超时。于是便用了状压。

毕竟这里列只有10行最多,就可以考虑根据列来进行状态。

我们用三维数组d[i][j][k]来表示到了第i行的时候,i行的选择是j,i-1行的选择是k。

这里可以简单理解,如果我们这里选了这个点架炮,那么除了这个点以外的地方,如果是最优解,加上他自然也是最优解。

当然这里的最优解其实是求得最大值。

而这里开三维数组是因为,他要占用两行,所以这一行能不能放最多和i-1,i-2相关,列的表示就用状态表示。

高地为1,平地为0,如果架了炮便成为1,这里预处理一下地图,判断这一行有多少位置能架炮,最开始准备用dfs来遍历,求位置的,后来看了大神的思路,发现这样太麻烦了,具体实现见代码

 

 

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const long long INF = 0x3f3f3f3f;
using namespace std;
int n, m;
char s[105][15];
int vis[1<<11];
int d[105][105][105];
int cnt[1 << 11];
int sum1[1 << 11];
int binary(int x)//这里求的是x有多少个1
{
	int sum2 = 0;
	while (x)
	{
		if (x & 1)
			sum2++;
		x >>= 1;
	}
	return sum2;
}
int main()
{
	cin >> n >> m;
	up(i, 0, n)
	{
		cin >> s[i];
		up(j, 0, m)
		{
			if(s[i][j] == 'H')
				vis[i] |= (1 << j);//向vis[i]中添加01地图
		}
	}
	memset(d, -1, sizeof(d));
	int len = 0;
	up(i, 0, (1 << m))
	{
		if (!(i&(i << 1)) && !(i&(i << 2)))//这里就是大神代码的精髓
//判断i是否和自己前一位还有前两位重合即不能同为1,作为预处理。
		{
			cnt[len] = i;//记录下这个状态的十进制值。(len代表了状态)
			sum1[len++] = binary(i);//有多少个1就可以架多少个炮
		}
	}
	up(i, 0, len)//处理第一行
	{
		if (!(vis[0] & cnt[i]))
		{
			d[0][i][0] = sum1[i];
		}
	}
	up(i, 1, n)
	{
		up(j, 0, len)
		{
			if (vis[i] & cnt[j]) continue;//和地图一样,能架炮
			up(k, 0, len)
			{
				if (cnt[j] & cnt[k])continue;//i-1行能放
				if (vis[i - 1] & cnt[k]) continue;

				up(s, 0, len)
				{
					if (cnt[j] & cnt[s])continue;//i-2行能放
					if (cnt[k] & cnt[s])continue;
					if (vis[i - 2] & cnt[s])continue;
					d[i][j][k] = max(d[i - 1][k][s] + sum1[j], d[i][j][k]);
				}
			}
		}
	}
	int temp = 0;
	up(i, 0, len)
	{
		up(j, 0, len)
		{
			temp = max(temp, d[n-1][i][j]);//最后一行,找到最大值。
		}
	}
	cout << temp;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值