状压dp本人做的题目真的不太多...至今还未理解到其中的精髓.所以以下的思路描述中有存在不当的地方希望能够指出.另外,有些地方说的比较复杂,因为本弱鸡
对这些东西不是很理解.....多写点有助于理解吧.
POJ 1185 经典状压dp 我队友这篇博文还不错.
思路:
首先,我们可以发现对于每一行的当前位置能不能放炮兵,只与他的上一行和上上一行
的炮兵位置有关系,所以要开一个三维数组转移关系.
0表示不放大炮,1表示放大炮,同样的,先要满足硬件条件,即有的地方不能放大炮,
然后就是每一行中不能有两个1的距离小于2(保证横着不互相攻击),这些要预先处理一下。然后就是
状态表示和转移的问题了,因为是和前两行的状态有关,所以要开个三维的数组来表示状态,当前行
的状态可由前两行的状态转移而来。即如果当前行的状态符合前两行的约束条件(不和前两行的大炮
互相攻击),则当前行的最大值就是上一个状态的值加上当前状态中1的个数(当前行放大炮的个数)
【状态表示】dp[i][j][k] 表示第i行为第j个状态,第i-1为第k个状态时的最大炮兵个数。
【状态转移方程】 dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][t]+num[j]);//num[j]为第j个状态中的二进制1的个数
【DP边界条件】dp[1][i][0] =num[i] 状态i能够满足第一行的硬件条件
(注意:这里的i指的是第i个状态,不是一个二进制数,开一个数组保存二进制状态)
思考:
对于这种有地形限制的,标记一个点其他周围都有影响的,一般都要预处理.将所有可能的状态和地形的限制预处理出来,每次枚举第i个状态的时候,要首先满足地形的限制,在满足各行各列之间的关系.
#include
#include
#include
#include
using namespace std;
const int maxn=111;
int dp[maxn][maxn][maxn];//dp[i][j][k] 第i行为第j个状态,第i-1行为第k个状态时的最大炮兵数
int status[maxn],num[maxn]; //status[i] 当前第i个状态的二进制 kk(1放大炮 0 不放).
//num[i]存放与status相对应的当前的第i个状态的二进制中有多少个1,即放了多少炮兵
char s[maxn];
int mp[maxn];//存放地形的限制,1表示不能放大炮,0表示可以放大炮.
int n,m;
int _count(int x)
{
int __=0;
while(x)
{
if(x&1)
__++;
x>>=1;
}
return __;
}
int main()
{
while(~scanf("%d %d",&n,&m))
{
memset(dp,-1,sizeof(dp));
memset(status,0,sizeof(status));
memset(num,0,sizeof(num));
memset(mp,0,sizeof(mp));
for(int i=1;i<=n;i++)
{
scanf("%s",s);
for(int j=0;j
西电oj 1038
思路:
这个题目和炮兵阵地那个题目很像很像,不过这个是要割出来2*2的.还是老规矩,我们先来确定一下需要几维.由于是2*2,那么每次割玻璃只会对下面一行有影响.也就是说当前行能否割,只与上一行的状态有关了,所以二维就足够了。
其次因为这是一个2*2的我们枚举1表示切方格的时候只能枚举四个中的一小块,所以这里我设当第i行第j位为1的时候,那么他就按照这样切,表示它和第i+1行的 j-1 j 四块,构成一个2*2.
01
00
那么我们最后只需要统计第n-1行的所有可能状态中二进制数最多的那个.另外我们发现它只对三个方向和自身地形,4个位置不能冲突.另外需要注意的就是按照我们这种的
切割方式它最前面永远不可能是1只能是0.所以我们枚举状态的时候只要枚举到(1<<(m-1))就可以了.
根据炮兵阵地的总结,存在这种地形限制问题的, 我们都需要进行预处理,即把所有可能的情况预处理出来,把自己地形的限制预处理出来,(能切割的地方为0,不能切割的地方为1).
最后给出状态转移方程:
dp[i][j]=max(dp[i][j],dp[i-1][k]+num[j])
//dp[i][j]表示第i行的第j个状态.(是所有可行状态中的第j个,并不代表一个二进制数)
#include
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1024;
int dp[maxn][maxn];
int status[maxn],num[maxn];
int mp[maxn];
int t,n,m;
int __(int x)
{
int _=0;
while(x)
{
if(x&1)
_++;
x>>=1;
}
return _;
}
int check_row(int r,int i)//判断第r行的第i个状态和r的地形是否冲突
{
int sta=status[i];
if(mp[r]&sta||mp[r+1]&sta||mp[r]&(sta<<1)||mp[r+1]&(sta<<1)) return 0;
return 1;
}
int check_sta(int i,int j)//判断第i个状态和第j个状态是否冲突。
{
int s1=status[i],s2=status[j];
if(s1&s2||s1&(s2<<1)||(s1<<1)&s2) return 0;
return 1;
}
int main()
{
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof(dp));
memset(mp,0,sizeof(mp));
memset(status,0,sizeof(status));
memset(num,0,sizeof(num));
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
int x;
for(int j=0;j