题目大意:一个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】P1131时态同步(树形DP)

    题目链接 甚矣吾衰也!这么简单的DP我都不会了 太恐怖了 树形DP,从子树里选出时间最长的来,剩下的调到这个最长时间即可. #include<cstdio> #include<cct ...

  2. spring中quartz的使用。【转http://www.cnblogs.com/kay/archive/2007/11/02/947372.html】

    注:从spring3到spring4改变 org.springframework.scheduling.quartz.CronTriggerBean org.springframework.sched ...

  3. 单点登录跳转失败(原因是 主票据申请子票据失败) asp.net 同站点下不同应用间不同版本Framework问题

    单点登录跳转失败(原因是 主票据申请子票据失败) asp.net 同站点下不同应用间不同版本Framework问题 今天遇到一个问题,在主站点现在配置的应用和主站点登录会话状态不能共享,进入子站点应用 ...

  4. 魔法森林(bzoj 3669)

    Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...

  5. curl抓取数据

    抓取数据的代码: $url='抓取数据的网站路径'; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); //参数为1表示传输数据,为0表示 ...

  6. eclipse 安卓虚拟机安装apk 及常见问题

    首先必须启动虚拟机然后如图操作:

  7. 记住密码后,密码框Password会自动带出数据

    一般登陆之后浏览器会询问是否记住密码,如果把密码记住在浏览器上,下次登陆的时候浏览器会把用户名和密码自动填充到登录页面.前段时间服务站平台的员工账号模块提测后,测试提出360浏览器记住密码后会自用把登 ...

  8. Java到底是值传递还是引用传递

    什么是按值传递,什么是按引用传递 按值调用(call by value) : 在参数传递过程中,形参和实参占用了两个完全不同的内存空间.形参所存储的内容是实参存储内容的一份拷贝. 按引用调用:在参数传 ...

  9. Nginx负载均衡配置实例(转)

    1.轮询 轮询即Round Robin,根据Nginx配置文件中的顺序,依次把客户端的Web请求分发到不同的后端服务器.配置的例子如下: http{ upstream sampleapp { serv ...

  10. jquery 禁用/启用滚动条

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...