题目大意:一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),在每一格平原地形上最多可以布置一支炮兵部队,能攻击到的区域:沿横向左右各两格,沿纵向上下各两格。保证任何两支炮兵部队之间不能互相攻击时,最多能放置的炮兵数。N<=100,N<=10

动规先要确定方向,我们规定其为从上到下。每一排的最优值与其前两排的各个炮兵的放置位置都有关,所以为了使得DP以后的排时能够找到其对应的前两排的各个炮兵的放置位置所对应的最优值,DP内的参数有:

  1. 当前的排数i
  2. 当前排的炮兵状态curS
  3. 上一排的炮兵状态prevS

定义上两排的炮兵状态为grandS,这样,对于每个满足二进制数内两个1距离超过2的curS,prevS,grandS(因为M是固定的,所以可以在DP前将其算好,叫做RowSs)递归式为:

foreach DP(i, curS, prevS) (curS属于i排平原 且 prevS属于i-1排平原 且 curS∩prevS=空),其值 = max foreach DP(i-1, prevS, grandS)+curS内1的数量 (grandS属于i-2排平原 且 curS∩grandS为空 且 prevS∩grandS为空)

curS内1的数量可以在算完RowSs时一起求出。

一切数组从0开始,DP开始时先特殊处理i=0和1的情况,避免以后在各种特判中搞晕。

DP要用三层滚动数组保存,否则应该会爆空间。

注意:

  • 计算RowSs的过程就用二进制的枚举子集,不要想其它方法浪费时间。
  • 每到一个i就要将其对应的滚动数组的那一层清空!!!!!!!!!!!!!!!!!!!!
  • 注意对N和M下定义,不要搞反了,否则再好的算法也只能得20分。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdarg>
#include <cstdlib>
#include <iostream>
#include <bitset>
using namespace std; const int MAX_N = 110, MAX_M = 10, NINF = 0xcfcfcfcf;
int Map[MAX_N], DP[3][1 << (MAX_M + 1)][1 << (MAX_M+1)], ColSs[1<<(MAX_M+1)], ColNums[1<<(MAX_M+1)];
char CMap[MAX_N][MAX_M];
int N, M, Scnt; #define LOOP0(i, n) for(int i=0; i<(n); i++)
#define LoopFrom(i, m, n) for(int i=(m); i<(n); i++)
#define Update(x, y) x=max(x, y)
#define In(B, A) (((B)&(A))==(B))
#define InterSect(x, y) ((x)&(y))
#define Join(A, x) ((A)|=(1<<(x))) int BCnt(int x)
{
int ans = 0;
while (x)
{
ans += (x & 1);
x >>= 1;
}
return ans;
} int CalColS(int *rowSs, int m)
{
int cnt = 0;
LOOP0(i, 1 << m)
if (!InterSect(i, i << 1) && (!InterSect(i, i << 2)))
{
rowSs[cnt++] = i;
ColNums[i] = BCnt(i);
}
return cnt;
} //S:state
int Proceed()
{
memset(DP, NINF, sizeof(DP));
LOOP0(j, Scnt)
{
int curS = ColSs[j];
if (In(curS, Map[0]))
{
DP[0][curS][0] = ColNums[curS];
}
}
LOOP0(j, Scnt)
{
int curS = ColSs[j];
if (In(curS, Map[1]))
{
LOOP0(k, Scnt)
{
int prevS = ColSs[k];
if (In(prevS, Map[0]) &&
!InterSect(prevS, curS))
{
DP[1][curS][prevS] = DP[0][prevS][0] + ColNums[curS];
}
}
}
}
LoopFrom(i,2,N)
{
memset(DP[i % 3], NINF, sizeof(DP[i % 3]));
LOOP0(j, Scnt)
{
int curS = ColSs[j];
if (In(curS, Map[i]))
{
LOOP0(k, Scnt)
{
int prevS = ColSs[k];
if (In(prevS,Map[i-1])&&
!InterSect(curS, prevS))
{
LOOP0(l, Scnt)
{
int grandS = ColSs[l];
if (In(grandS, Map[i - 2]) &&
!InterSect(curS, grandS) &&
!InterSect(prevS, grandS))
{
Update(DP[i % 3][curS][prevS],
DP[(i - 1) % 3][prevS][grandS] + ColNums[curS]);
}
}
}
}
}
}
}
int ans = 0;
LOOP0(j, Scnt)
{
int curS = ColSs[j];
if (In(curS, Map[N-1]))
{
LOOP0(k, Scnt)
{
int prevS = ColSs[k];
if (In(prevS, Map[N - 2]) && !InterSect(curS, prevS))
Update(ans, DP[(N-1)%3][curS][prevS]);
}
}
}
return ans;
} int main()
{
#ifdef _DEBUG
freopen("c:\\noi\\source\\input.txt", "r", stdin);
#endif
scanf("%d%d", &N, &M);
LOOP0(i, N)
{
scanf("\n%s", i+CMap);
LOOP0(j, M)
if (CMap[i][j] == 'P')
Join(Map[i], j);
}
Scnt = CalColS(ColSs, M);
printf("%d\n", Proceed());
return 0;
}

  

luogu2704 炮兵阵地 状态压缩DP的更多相关文章

  1. POJ1185 - 炮兵阵地(状态压缩DP)

    题目大意 中文的..直接搬过来... 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平 ...

  2. POJ1185炮兵阵地(状态压缩 + dp)

    题目链接 题意:给出一张n * m的地图,其中 有的地方能放大炮,有的地方不能,大炮与上下左右两个单位范围内会相互攻击,问最多能放几个大炮 能放大炮为1不能放大炮为0,把每一行看做一个状态,要除去同一 ...

  3. poj 1185 炮兵阵地 状态压缩dp

    思路:定义一个三维数组dp[x][i][j]其中x为now和pre两种状态,now表示当前两行最优解,pre表示出了本行外,前两行的最优解.那么状态转移方程为 dp[now][j][k]=max(dp ...

  4. POJ 3254 炮兵阵地(状态压缩DP)

    题意:由方格组成的矩阵,每个方格可以放大炮用P表示,不可以放大炮用H表示,求放最多的大炮,大炮与大炮间不会互相攻击.大炮的攻击范围为两个方格. 分析:这次当前行的状态不仅和上一行有关,还和上上行有关, ...

  5. POJ - 1185 炮兵阵地 (状态压缩)

    题目大意:中文题目就不多说大意了 解题思路: 1.每行最多仅仅有十个位置,且不是山地就是平原,那么就能够用1表示山地,0表示平原,将每一行的状态进行压缩了 2.接着找出每行能放炮兵的状态.先不考虑其它 ...

  6. [P2704][NOI2001]炮兵阵地 (状态压缩)

    最近抄状压的代码…… 然后盯上了这个题目 调试了一个晚上,终于A了 但是是对着宝典打的,我依然不懂状态压缩 那么下一步先把装压放一放,学一下树形DP吧 #include<cstdio> # ...

  7. POJ1185 炮兵阵地 状态压缩

    因为不知道不同的博客怎么转,就把别人的复制过来了,这个题解写的非常好,原地址为: http://hi.baidu.com/wangxustf/item/9138f80ce2292b8903ce1bc7 ...

  8. luogu P2704 炮兵阵地(经典状态压缩DP)

    方格有m*n个格子,一共有2^(m+n)种排列,很显然不能使用暴力法,因而选用动态规划求解. 求解DP问题一般有3步,即定义出一个状态 求出状态转移方程 再用算法实现.多数DP题难youguan点在于 ...

  9. 浅谈状态压缩DP

    浅谈状态压缩DP 本篇随笔简单讲解一下信息学奥林匹克竞赛中的状态压缩动态规划相关知识点.在算法竞赛中,状压\(DP\)是非常常见的动规类型.不仅如此,不仅是状压\(DP\),状压还是很多其他题目的处理 ...

随机推荐

  1. 【Luogu】P1013进制位(搜索)

    题目链接在这里 这题和虫食算比较类似.做完这道题可以去做虫食算.都是搜索一类的题. 这样 我们分析题目可以发现进制只可能是字母的个数,也就是n-1.为什么? 因为题目要求完整的加法表才算数.如果进制低 ...

  2. 刷题总结——动态逆序对(bzoj3295)

    题目: Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素 ...

  3. 解决 sqlalchemy 报错:(1193, "Unknown system variable 'tx_isolation'")

    1出现此报错的原因是使用的mysql8.0 以前用的是:tx_isolation 现在用是: transaction_isolation a.通过升级 sqlalchemy 的方法可以解决此问题, p ...

  4. Office 中的各种小tips(更新中)

    1.Word 中打字输入会擦掉之后原有字符,出现“吃字”的情况? 要将“改写”切换为“插入”,最简单的方法就是点击键盘上小键盘旁边的“insert”键. 其实仔细观察的话,在word文档下方,会看到如 ...

  5. cf287D Shifting

    John Doe has found the beautiful permutation formula. Let's take permutation p = p1, p2, ..., pn. Le ...

  6. 【2017YYHS WC】

    因为本葳蕤分数太低去不了WC,只能同去WC的各位大爷一起训练一波,就称作是YYHS WC吧,其实就是WC难度的多校 day1:早上8:30考的试,下午1:00去吃中饭 T1:考场打得暴力结果矩阵乘法后 ...

  7. hdu 4778 Gems Fight! 状压dp

    转自wdd :http://blog.csdn.net/u010535824/article/details/38540835 题目链接:hdu 4778 状压DP 用DP[i]表示从i状态选到结束得 ...

  8. Objective-C NSString的常用用法

    //1.创建常量字符串. NSString *astring = @"This is a String!";   //2.创建空字符串,给予赋值. NSString *astrin ...

  9. 洛谷——P1560 [USACO5.2]蜗牛的旅行Snail Trails

    P1560 [USACO5.2]蜗牛的旅行Snail Trails 题目描述 萨丽·斯内尔(Sally Snail,蜗牛)喜欢在N x N 的棋盘上闲逛(1 < n <= 120). 她总 ...

  10. Centos常用命名

    1.关机 (系统的关机.重启以及登出 ) 的命令 shutdown -h now 关闭系统(1) init 0 关闭系统(2) telinit 0 关闭系统(3) shutdown -h hours: ...