NOI2001炮兵阵地

题目传送门

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个,这样数组就足够用了

具体实现方法如下

 1 void init()
 2 {
 3   for(int i=0;i<(1<<m);i++)
 4     {
 5       if(!(i&(i>>1)) && !(i&(i>>2)) && !((i>>1)&(i>>2)))
 6       {
 7         can[++cnt]=i;
 8         num[cnt]=get(i);
 9       }
10     }
11   //cout<<cnt<<endl;-------算出来最大只有60
12 }

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

 1   in(n);in(m);
 2   for(int i=1;i<=n;i++)
 3     {
 4       for(int j=1;j<=m;j++)
 5     {
 6       char ch;
 7       cin>>ch;
 8       if(ch==‘H‘) cur[i]|=1<<(j-1);//记得反过来存
 9     }
10     }

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

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

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

 1   for(int i=1;i<=cnt;i++)//不用判断上一行
 2     {
 3       if(!(cur[1]&can[i]))
 4     {
 5       dp[1][i][0]=max(dp[1][i][0],num[i]);
 6       tot=max(tot,dp[1][i][0]);
 7     }
 8     }
 9   //处理第一行
10   for(int i=1;i<=cnt;i++)//只用判断上一行
11     {
12       if(!(cur[2]&can[i]))
13     {
14       for(int j=1;j<=cnt;j++)
15         {
16           if(!(cur[1]&can[j]) && !(can[i]&can[j]))
17         {
18           dp[2][i][j]=max(dp[2][i][j],dp[1][j][0]+num[i]);
19           tot=max(tot,dp[2][i][j]);
20         }
21         }
22     }
23     }
24   //处理第二行

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

最后上个完整版代码

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cmath>
  4 #include<string>
  5 #include<cstring>
  6 #define in(i) (i=read())
  7 using namespace std;
  8 int read()
  9 {
 10   int ans=0,f=1;
 11   char i=getchar();
 12   while(i<‘0‘||i>‘9‘)
 13     {
 14       if(i==‘-‘) f=-1;
 15       i=getchar();
 16     }
 17   while(i>=‘0‘&&i<=‘9‘)
 18     {
 19       ans=(ans<<1)+(ans<<3)+i-‘0‘;
 20       i=getchar();
 21     }
 22   return ans*f;
 23 }
 24 int dp[110][65][65];
 25 int can[65];
 26 int cur[110];
 27 int num[65];
 28 int n,m,cnt,tot;
 29 int get(int x)
 30 {
 31   int ans=0;
 32   for(int i=1;i<=m;i++)
 33     {
 34       if((x&(1<<(i-1)))!=0) ans++;//判断是否可以部署军队,若可以则ans++
 35     }
 36   return ans;
 37 }
 38 void init()
 39 {
 40   for(int i=0;i<(1<<m);i++)
 41     {
 42       if(!(i&(i>>1)) && !(i&(i>>2)) && !((i>>1)&(i>>2)))//相邻之间没有1
 43     {
 44       can[++cnt]=i;
 45       num[cnt]=get(i);
 46     }
 47     }
 48   //cout<<cnt<<endl;-------算出来最大只有60
 49 }
 50 int main()
 51 {
 52   in(n);in(m);
 53   for(int i=1;i<=n;i++)
 54     {
 55       for(int j=1;j<=m;j++)
 56     {
 57       char ch;
 58       cin>>ch;
 59       if(ch==‘H‘) cur[i]|=1<<(j-1);//记得反过来存
 60     }
 61     }
 62   init();//预处理
 63   for(int i=1;i<=cnt;i++)//不用判断上一行
 64     {
 65       if(!(cur[1]&can[i]))
 66     {
 67       dp[1][i][0]=max(dp[1][i][0],num[i]);
 68       tot=max(tot,dp[1][i][0]);
 69     }
 70     }
 71   //处理第一行
 72   for(int i=1;i<=cnt;i++)//只用判断上一行
 73     {
 74       if(!(cur[2]&can[i]))
 75     {
 76       for(int j=1;j<=cnt;j++)
 77         {
 78           if(!(cur[1]&can[j]) && !(can[i]&can[j]))
 79         {
 80           dp[2][i][j]=max(dp[2][i][j],dp[1][j][0]+num[i]);
 81           tot=max(tot,dp[2][i][j]);
 82         }
 83         }
 84     }
 85     }
 86   //处理第二行
 87   for(int i=3;i<=n;i++)//枚举行数,对后n-2行进行处理
 88     {
 89       for(int j=1;j<=cnt;j++)//枚举第i行的状态
 90     {
 91       if(!(cur[i]&can[j]))//判断本行是否有冲突
 92         {
 93           for(int k=1;k<=cnt;k++)//若无则枚举下一行的状态
 94         {
 95           if(!(cur[i-1]&can[k]))//判断上一行状态是否有冲突
 96             {
 97               for(int l=1;l<=cnt;l++)//枚举上上一行状态
 98             {
 99               if(!(cur[i-2]&can[l]))
100                 {
101                   if(!(can[j]&can[k]) && !(can[j]&can[l]) && !(can[k]&can[l]))//若都无冲突
102                 {
103                   dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+num[j]);//状态转移
104                   tot=max(tot,dp[i][j][k]);
105                 }
106                 }
107             }
108             }
109         }
110         }
111     }
112     }
113   cout<<tot<<endl;
114   return 0;
115 }

原文地址:https://www.cnblogs.com/real-l/p/8587798.html

时间: 2024-08-30 08:25:54

NOI2001炮兵阵地的相关文章

状压DP NOI2001 炮兵阵地

司令部的将军们打算在N × M的网格地图上部署他们的炮兵部队.一个N × M的地图由N行M列组成,地图的每一格可能是山地(用"H"表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格.图上其它白色网格均攻击不到.从图上可见炮兵

NOI2001 炮兵阵地

一道非常有意思的题目 很久之前考过 但那时候好像只会打裸搜索(捂脸跑 后来看题解的时候也是没有学状压的所以算是闲置了很久没动的题 昨天看到的时候第一反应是m<=10所以压m然后跑1-n枚举每一行 但是非常遗憾的是我一直在想横行怎么判断合法 所以比较sb的我想了好久都没想出来 于是又很怂逼地去看了题解(我怎么这么怂 横行合法只要位移然后与一下就可以判断了 竖行的合法就是根据上面两行的状态转移 f[i][j][k]表示的是第i行上一行状态为j本行状态为k的炮塔数量 特别注意零也是一种情况!!!昨天被

poj1185 [NOI2001]炮兵阵地

http://poj.org/problem?id=1185 三维装压dp,压缩上一行状态与本行状态,枚举上两行状态转移 第一维可以滚掉,实际复杂度只枚举符合情况的情况,每行状态不会超过60并非$2^M$,证明参见组合数 #include<cstdio> #include<cstring> #include<algorithm> const int maxnm = 1001; const int maxn = 11; int n,m; char a[maxn]; int

【NOI2001】炮兵阵地

[题目描述] 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击 范围如图中黑色区域所示: 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格.图上其它白色网格均攻击不到.从图

POJ 1185 炮兵阵地 状压dp

http://poj.org/problem?id=1185 经典题目不必多说,直接贴代码. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int n, m, cnt, size; 7 int a[110], st[70], ct[70]; 8 char str[15]; 9 int f[110][70][70]; 10 void init(

xtu DP Training C.炮兵阵地

炮兵阵地 Time Limit: 2000ms Memory Limit: 65536KB This problem will be judged on PKU. Original ID: 118564-bit integer IO format: %lld      Java class name: Main 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表

状压DP 洛谷P2704 炮兵阵地

P2704 炮兵阵地 题目描述 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格.图上其它白色网格

炮兵阵地

poj1185:http://poj.org/problem?id=1185 题意:这道题太经典了,看到题目就知道题意,故题意略. 题解:经典的状压dp.以前觉得dp是个很难的东西,做了这一题之后发现,dp其实根本不难,真的,而且很好打. 思维过程:拿到题之后,由于之前做过,所以知道是状压dp.接着想怎么解决.普通的dp,不要考虑障碍,这一题中有障碍限制.多以第一步要处理这个障碍.想到了用这样的方式来解决.把出现h的地方标记为1,其余记0,从右到左,从低位到高位,求出这个数,然后枚举状态的时候,

状态压缩动态规划 -- 炮兵阵地

司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队,一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图.在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队):一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格.图上其它白色网格均攻击不到.从图上可见炮兵的攻击