之前做这道题第一反应是用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;
}