题目传送门

PS:本道题目建议在对状压dp有一定了解的基础下学习,如有不懂可以先去学一下状压dp入门


题目大意:给你n*m个格子,有些格子可以用来部署军队,用P表示,有些则不能,用H表示,如果在一个格子上部署了军队,则上下左右各2个格子都不能部署军队,也就是呈十字架状,看到数据范围(n<=100,m<=10)很容易想到使用状压dp,因为m列数最大只有10,我们可以压缩每一行的状态,最大只有(1<<10)-1种状态,但是由于这一行的状态对下一行以及下两行都有影响,我们需要一个三维数组来保存状态,dp[i][j][k],代表第i行状态为j,上一行状态为k最大能部署的军队数,那么我们应该就能推出状态转移方程:

dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+num[i])(l为上两行的状态,num[i]为状态为i时能部署的军队数)


但是有一个问题,就是n最大为10,但是我们的数组好像还是开不下,dp[110][1<<10][1<10]显然是超内存的,怎么办呢?我们其实可以抓住如果在一个格子上部署军队则左右两边各2个格子都不能部署军队这一点来预处理进而达到缩小内存的方法,那么怎么预处理呢,其实和那道入门题的判断方法是一样的,我们将某一个状态分别右移1位,2位,这样便得到三个状态,然后两两相与,如果都等于0说明这个状态是合法的,那么这样下来最多的状态数也只有60个,这样数组就足够用了

具体实现方法如下

in(n);in(m);
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
char ch;
cin>>ch;
if(ch=='H') cur[i]|=<<(j-);//记得反过来存
}
}

然后就是保存每一行是否可以部署军队的操作,这里记得要将P和H反过来存,若为正着存,则后面不部署军队的情况是考虑不到的,应该很好理解吧

in(n);in(m);
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
char ch;
cin>>ch;
if(ch=='H') cur[i]|=<<(j-);//记得反过来存
}
}

然后就是dp部分了,因为从第三行开始,后面的行的状态都与前面一行及两行的状态有关,因此我们先处理出第一行以及第二行的状态,以便后面的递推

第一行的时候我们是不需要判断前面的行数的,因此只需要判断这一行的状态是否和合法就行了

而第二行的时候我们则需要根据在第二行状态合法的前提下还需要根据第一行的状态看有没有冲突的的情况,这种情况是要舍去的,其他的就没什么好讲的了

for(int i=;i<=cnt;i++)//不用判断上一行
{
if(!(cur[]&can[i]))
{
dp[][i][]=max(dp[][i][],num[i]);
tot=max(tot,dp[][i][]);
}
}
//处理第一行
for(int i=;i<=cnt;i++)//只用判断上一行
{
if(!(cur[]&can[i]))
{
for(int j=;j<=cnt;j++)
{
if(!(cur[]&can[j]) && !(can[i]&can[j]))
{
dp[][i][j]=max(dp[][i][j],dp[][j][]+num[i]);
tot=max(tot,dp[][i][j]);
}
}
}
}
//处理第二行

其实后面的行数跟前面的判断差不多,就是先判断本行再判断上一行,再判断上两行,最判断这三个状态有没有冲突,只要有一个存在冲突,这种状态就是不合法的,那么我们就把它舍去

最后上个完整版代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#define in(i) (i=read())
using namespace std;
int read()
{
int ans=,f=; char i=getchar();
while(i<''||i>'') {if(i=='-') f=-; i=getchar();}
while(i>=''&&i<='') ans=(ans<<)+(ans<<)+i-'',i=getchar();
return ans*f;
}
int dp[][][];
int can[];
int cur[];
int num[];
int n,m,cnt,tot;
int get(int x)
{
int ans=;
for(int i=;i<=m;i++)
if((x&(<<(i-)))!=) ans++;//判断是否可以部署军队,若可以则ans++
return ans;
}
void init()
{
for(int i=;i<(<<m);i++) {
if(!(i&(i>>)) && !(i&(i>>)) && !((i>>)&(i>>))){//相邻之间没有1
can[++cnt]=i;
num[cnt]=get(i);
}
}
//cout<<cnt<<endl;-------算出来最大只有60
}
int main()
{
in(n);in(m);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++) {
char ch; cin>>ch;
if(ch=='H') cur[i]|=<<(j-);//记得反过来存
}
init();//预处理
for(int i=;i<=cnt;i++)//不用判断上一行
if(!(cur[]&can[i])){
dp[][i][]=max(dp[][i][],num[i]);
tot=max(tot,dp[][i][]);
}
//处理第一行
for(int i=;i<=cnt;i++)//只用判断上一行
{
if((cur[]&can[i])) continue;
for(int j=;j<=cnt;j++)
if(!(cur[]&can[j]) && !(can[i]&can[j])) {
dp[][i][j]=max(dp[][i][j],dp[][j][]+num[i]);
tot=max(tot,dp[][i][j]);
}
}
//处理第二行
for(int i=;i<=n;i++)//枚举行数,对后n-2行进行处理
for(int j=;j<=cnt;j++){//枚举第i行的状态
if((cur[i]&can[j])) continue;//判断本行是否有冲突
for(int k=;k<=cnt;k++){//若无则枚举下一行的状态
if((cur[i-]&can[k])) continue;//判断上一行状态是否有冲突
for(int l=;l<=cnt;l++){//枚举上上一行状态
if((cur[i-]&can[l])) continue;
if(!(can[j]&can[k]) && !(can[j]&can[l]) && !(can[k]&can[l])){//若都无冲突
dp[i][j][k]=max(dp[i][j][k],dp[i-][k][l]+num[j]);//状态转移
tot=max(tot,dp[i][j][k]);
}
}
}
}
cout<<tot<<endl;
return ;
}
												

NOI2001炮兵阵地的更多相关文章

  1. [洛谷P2704] [NOI2001]炮兵阵地

    洛谷题目链接:[NOI2001]炮兵阵地 题目描述 司令部的将军们打算在NM的网格地图上部署他们的炮兵部队.一个NM的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示), ...

  2. C++ 洛谷 P2704 [NOI2001]炮兵阵地

    P2704 [NOI2001]炮兵阵地 没学状压DP的看一下 此题意思很简单,如下图,就是十字架上的不能有两个点放炮兵. 在做此题前,先做一下玉米田 玉米田题解 分析: 而m即一行的个数小于等于10, ...

  3. P2704 [NOI2001]炮兵阵地 (状压DP)

    题目: P2704 [NOI2001]炮兵阵地 解析: 和互不侵犯一样 就是多了一格 用\(f[i][j][k]\)表示第i行,上一行状态为\(j\),上上行状态为\(k\)的最多的可以放的炮兵 发现 ...

  4. 洛谷P2704 [NOI2001]炮兵阵地 [状压DP]

    题目传送门 炮兵阵地 题目描述 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图 ...

  5. [Poj1185][Noi2001]炮兵阵地(状压dp)

    炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 29476   Accepted: 11411 Descriptio ...

  6. P2704 [NOI2001]炮兵阵地

    题目描述 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图.在每一格平原地形上最 ...

  7. [NOI2001]炮兵阵地 【状压DP】

    #\(\color{red}{\mathcal{Description}}\) \(Link\) 司令部的将军们打算在\(N \times M\)的网格地图上部署他们的炮兵部队.一个\(N \time ...

  8. [NOI2001]炮兵阵地 状压DP

    题面: 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图.在每一格平原地形上最多 ...

  9. Luogu 2704 [NOI2001]炮兵阵地

    唔,想到了状压之后就不会了……实在是菜 考虑压两行,设$f_{i, j, k}$表示当前到第$i$行,上一行是$j$状态,前一行是$k$状态的最多能放的炮兵的数量. 发现第一维还可以滚掉,好像可以转移 ...

  10. 洛谷2704 [NOI2001]炮兵阵地

    题目戳这里 Solution 状压DP很好的入门题,用熟练位运算貌似也没那么难. 首先分析一下题目: 看见n=100,m=10,立马就想到了状压,看起来也像DP,所以我们还是采用行号为阶段的状压DP. ...

随机推荐

  1. CSS布局(二) 盒子模型属性

    盒子模型的属性 宽高width/height 在CSS中,可以对任何块级元素设置显式高度. 如果指定高度大于显示内容所需高度,多余的高度会产生一个视觉效果,就好像有额外的内边距一样: 如果指定高度小于 ...

  2. 从flexible.js引入高德地图谈起的移动端适配

    曾几何时,前端还仅仅是PC端的.随着移动时代的兴起,h5及css3的推陈出新.前端的领域慢慢的由传统的pc端转入了移动端,这也导致了前端这一职业在风口的一段时间出尽了风头. 从开始的惶恐和无从下手,慢 ...

  3. 重绘(redraw或repaint),重排(reflow)

    浏览器运行机制图: 浏览器的运行机制:layout:布局: 1.构建DOM树(parse):渲染引擎解析HTML文档,首先将标签转换成DOM树中的DOM node(包括js生成的标签)生成内容树(Co ...

  4. 为什么要使用Docker?

    作为一种新兴的虚拟化方式,Docker跟传统的虚拟化方式相比具有众多的优势. 更高效的利用系统资源 由于容器不需要进行硬件虚拟及运行完整操作系统等额外开销,Docker对系统资源的利用率更高.无论是应 ...

  5. 【2016北京集训测试赛(十六)】 River (最大流)

    Description  Special Judge Hint 注意是全程不能经过两个相同的景点,并且一天的开始和结束不能用同样的交通方式. 题解 题目大意:给定两组点,每组有$n$个点,有若干条跨组 ...

  6. Kaggle新手入门之路

    学完了Coursera上Andrew Ng的Machine Learning后,迫不及待地想去参加一场Kaggle的比赛,却发现从理论到实践的转变实在是太困难了,在此记录学习过程. 一:安装Anaco ...

  7. rpm检验是否被改动过

    参考原文http://vbird.dic.ksu.edu.tw/linux_basic/0520rpm_and_srpm.php#rpmmanager_verify   rpm -qVa   (当然可 ...

  8. js中reduce()用法

    reduce()方法 定义:reduce()接收一个函数作为累加器,数组中每个值(从左到右)开始缩减,最终计算为一个值 对于空数组不执行回调函数 语法:array.reduce(function(to ...

  9. mysql一些使用技巧

    1.查看系统帮助文档:HELP contents; 2.查看所有支持的数据类型:HELP Data Types; 3.查看对应的数据类型的详细信息:HELP 类型:如HELP INT; 4.查看存储引 ...

  10. FusionCharts多数据验证饼图label是否重叠

    昨天,有人问我一个问题:由于饼图的数据太多,label标签上的汉字过多,导致重叠,该怎么解决? 今天我用大量的数据,label标签的字符也很多,但是通过验证没有发现有重叠的情况啊! 1.验证的JSP页 ...