1前言
本文是题解,不会介绍什么是状压dp,萌新请进我主页有介绍状压dp
作者是蒟蒻,提供的思路及标程没有滚动数组等优化,但是足以在洛谷评测机通过
2问题
如下图
翻译一下吧,就是在n*m的方格子里,摆放尽可能多形式如下的十字形物品
00100
00100
11111
00100
00100
可以超出边界,但是互相之间不可以冲突,有些格子不能放,求最多能放多少
这个数据范围,这用背包有后效性,就差把状压dp写题面里了…
3状态设置
我们先考虑直接压,n行m列,但是我们可以发现n非常大,以至于不压m列,只压n行都存不下了
但是m好像小一些
我们舍弃n行,直接用一维数组存n
我们还可以发现,只有上两行的会影响当前行
但是好像压缩三行也有点困难…
因为n作为数组的一维,我们可以只压两行,再用上一行推前两行,这就有递推性质,就这么定了
4属性及转移
属性显然为MAX,求最大值
我们发现,前两行的不同情况都对当前情况有影响
我们枚举三行,不要忘了n
设当前行为i,当前行状态为j,前一行状态为k,前两行状态为l,i二进制位1的个数为count(i)
得状态转移方程dp[i][j][k] = max(dp[i][j][k],dp[i-1][k][l]+count(i));
这时间复杂度大约2^36,能过才怪
5优化
不要忘了不是所有状态均合法,所以我们还有需要特判的步骤没做,在这一步优化吧
我们一步一步优化
优化1
不是所有位置都可以摆放炮兵
我们读入的是字符串,转换为01串,1为山地,然后直接状压
当前状态1为选择,而山地又不能选择,这下可以用与运算了,与出来不为0就跳过,且本方法对三行都适用
优化2
三行中,每个炮兵没有两个在同一列,我们依旧与运算,同上,与出来不为0就跳过,又快了好多
优化3
每一行的炮兵之间间隔至少为2
(1)作者方法
我考虑了lowbit,将每行lowbit算出,相邻之间,设小的为i,大的为j
i左移两位,如果相等或i>j,距离就小于2了
作者是树状数组学魔怔了,还有更快的方法
(2)常规方法
设当前行状态压缩后为i
我们用左移,分别将i左移一位和左移两位,都与一下,不为0就跳过
左移的本质就是将二进制位向左移动,而且是所有二进制位,一次成型,推荐这种方法,常数小
附代码(c++);
#include<bits/stdc++.h>
using namespace std;
const int N = 118,M = (1<<10)+200;
int dp[N][M][M];//本行和上一行,具有递归性质,时间空间乘上两个2^10=2^20
int n,m,ans;
string s;
int a[N];
int lowbit(