【浮*光】 #状压DP# NOI2001&洛谷p2704 炮兵阵地

版权声明:本文为博主原创文章,未经博主允许不得转载qwq。https://blog.csdn.net/flora715 https://blog.csdn.net/flora715/article/details/82428940

【NOI2001&洛谷p2704】炮兵阵地

  • 一个N*M的地图,每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示)。
  • 在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队)。
  • 每个部队能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。
  • 两支炮兵部队之间不能互相攻击,即任何炮兵部队都不在其他部队的攻击范围内。
  • 在整个地图区域内最多能够摆放多少我军的炮兵部队。

【思路分析】

用每个m位二进制数表示一行的状态:

每个二进制数的第k位为1表示在第k列上放置部队。

预处理出集合S(sum[ ]),储存“相邻两个1的距离不小于3”的所有M位二进制数。

int sums[(1<<10)];
//sums[x]表示x的二进制表示中1的个数。(int)
bool vaild[109][1<<11];

vaild[i][x]表示满足预处理条件的情况下,x的二进制表示中,

所有1的位置对应在地图第i行中,都是能使用的平原。(bool)

↑↑↑【有障碍时】存储行数,判断该行某状态是否成立。

也可以不使用,用a[109]记录每行的初始可行状态(是二进制数转化为十进制的数)。

判断的时候,只用在每次循环时特判相对应的S&a[i]。 

int ff[109][1<<11][1<<11]; //2048*2048*109??? ---> 所以要用滚动数组啊qwq
//f[i][j][k]表示第i-1行的状态是j,第i行状态是k时,前i行最多放置的炮兵数。

int f[4][(1<<11)][(1<<11)]={0}; //【滚动数组】对于每一行,只用记录前两行的状态。

【代码实现】

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;

//用每个m位二进制数表示一行的状态:
//每个二进制数的第k位为1表示在第k列上放置部队。

/*【p2704】炮兵阵地
一个N*M的地图,每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示)。
在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队)。
每个部队能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。
两支炮兵部队之间不能互相攻击,即任何炮兵部队都不在其他部队的攻击范围内。
在整个地图区域内最多能够摆放多少我军的炮兵部队。*/

//预处理出集合S,储存“相邻两个1的距离不小于3”的所有M位二进制数。

int sums[(1<<10)];
//sums[x]表示x的二进制表示中1的个数。(int)

//bool vaild[109][1<<11];
//vaild[i][x]表示满足预处理条件的情况下,x的二进制表示中,
//所有1的位置对应在地图第i行中,都是能使用的平原。(bool)
//↑↑↑【有障碍时】存储行数,判断该行某状态是否成立。
//也可以不使用,用a[109]记录每行的初始可行状态(是二进制数转化为十进制的数)。
//判断的时候,只用在每次循环时特判相对应的S&a[i]。

//int ff[109][1<<11][1<<11]; //2048*2048*109??? ---> 所以要用滚动数组啊qwq
//f[i][j][k]表示第i-1行的状态是j,第i行状态是k时,前i行最多放置的炮兵数。

int f[4][(1<<11)][(1<<11)]={0}; //【滚动数组】对于每一行,只用记录前两行的状态。

int n,m,a[109],anss=0; //a[109]记录每行的初始可行状态(是二进制数转化为十进制的数)

void reads(int &x){
  int fx=1;x=0;char s=getchar();
  while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();}
  while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
  x*=fx; 
}

int getsum(int S){ //当前状态 S 里面包含几个 1
  int tot=0;
  while(S) {if(S&1) tot++; S>>=1;}
  return tot;
} //【坑点!!】这个一定要写成函数,要不然会TLE QAQ

int main(){
  reads(n); reads(m); char ss;
  
  for(int i=0;i<n;i++) //注意,状压DP中最好都用0开始
    for(int j=0;j<m;j++){ //用a[i]记录每行的初始可行状态
      cin>>ss; a[i]<<=1; //二进制数a[i]每次向前移一位
      a[i]+=(ss=='H'?1:0); //记录障碍的位置
    }

  memset(sums,0,sizeof(sums));

  for(int i=0;i<(1<<m);i++) //对于每个状态,记录每行放置的部队数
    sums[i]=getsum(i); //初始化sum数组

  for(int i=0;i<(1<<m);i++) //【预处理】初始化第一行(行数编号是0)
    if(!(i&a[0] || (i&(i<<1)) || (i&(i<<2)))) //这些条件都不能成立
      f[0][i][0]=sums[i]; //第一行要特殊判定(因为没有1-2=-1行)

  for(int j=0;j<(1<<m);j++) //【预处理】初始化第二行(行数编号是1)
    for(int k=0;k<(1<<m);k++) //j是上一行,k是这一行
      if(!(j&k || j&a[0] || k&a[1] || (j&(j<<1)) || (j&(j<<2)) 
          || (k&(k<<1)) || (k&(k<<2)) ) )
        f[1][j][k]=sums[k]+sums[j]; //第二行要特殊判定(因为没有2-2=0行)

  for(int i=2;i<n;i++) //枚举行数
    for(int j=0;j<(1<<m);j++){ //【预处理】“相邻两个1的距离不小于3”的所有M位二进制数
      if(j&a[i-1] || (j&(j<<1)) || (j&(j<<2))) continue;  //特判
      for(int k=0;k<(1<<m);k++){ //j是上一行,k是这一行
        if(k&a[i] || j&k || (k&(k<<1)) || (k&(k<<2))) continue; //特判
        for(int FL=0;FL<(1<<m);FL++){ //FL是上上一行
          if(FL&j || FL&k || FL&a[i-2] || (FL&(FL<<1)) || (FL&(FL<<2))) continue;   
          f[i%3][j][k]=max(f[i%3][j][k],f[(i-1)%3][FL][j]+sums[k]);
        }
      }
    } 

  for(int j=0;j<(1<<m);j++)
    for(int k=0;k<(1<<m);k++)
      anss=max(anss,f[(n-1)%3][j][k]); //结束状态可以是最后一行(编号n-1)的任何状态
  
  cout<<anss<<endl;
  return 0;
}

                                                          ——时间划过风的轨迹,那个少年,还在等你。

猜你喜欢

转载自blog.csdn.net/flora715/article/details/82428940
今日推荐