状压DP

来源:互联网 发布:淘宝日本签证代办 编辑:程序博客网 时间:2024/05/21 17:51
题目:链接
炮兵阵地
Time Limit: 2000MS Memory Limit: 65536KTotal Submissions: 22877 Accepted: 8849

Description

司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队。一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用"P"表示),如下图。在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示: 

如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。图上其它白色网格均攻击不到。从图上可见炮兵的攻击范围不受地形的影响。 
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。 

Input

第一行包含两个由空格分割开的正整数,分别表示N和M; 
接下来的N行,每一行含有连续的M个字符('P'或者'H'),中间没有空格。按顺序表示地图中每一行的数据。N <= 100;M <= 10。

Output

仅一行,包含一个整数K,表示最多能摆放的炮兵部队的数量。

Sample Input

5 4PHPPPPHHPPPPPHPPPHHP

Sample Output

6


题意:中文题意不必多说。

题解:状态压缩dp:把字符等多种状态用二进制表示,减少空间复杂度,由于dp需要循环,就减少了时间复杂度。

          滚动数组也是减少复杂度的一个有利工具。由于此题中心得状态只与之前的一个状态有关,所以就只用保存             两种两种状态。

          本题的转移方程:dp[i][j][k]=max( dp[i][j][k], dp[i-1][k][l]+num[i] )    i表示当前行,j表示当前行所取的状态, k表示上一行的状态,l表示上上一行的状态。

 

[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<iostream>  
  4. #include<algorithm>  
  5. #include<cmath>  
  6. #include<queue>  
  7. #include<cstdlib>  
  8. #include<string>  
  9. using namespace std;  
  10.   
  11. #define N 150  
  12. #define inf 99999999  
  13.   
  14. int num[65];  
  15. int dp[2][65][65];  
  16. int ma[105];  
  17. int st[65];  
  18.   
  19. int n,m,ns;  
  20.   
  21. int getnum(int a)                 //每个状态中1的个数  
  22. {  
  23.     int res=0;  
  24.     for(int i=0;i<10;i++)  
  25.     {  
  26.         if(a&(1<<i))  
  27.         res++;  
  28.     }  
  29.     return res;  
  30. }  
  31.   
  32. void init()                    //从1024个状态中选出符合要求的60个状态。状态压缩  
  33. {  
  34.     ns=0;  
  35.     memset(num,0,sizeof(num));  
  36.     memset(st,0,sizeof(st));  
  37.     for(int i=0;i<1023;i++)  
  38.     {  
  39.         if(!((i<<1)&i)&&!((i<<2)&i))  
  40.         {  
  41.             st[ns]=i;  
  42.             num[ns++]=getnum(i);    //保存数据,以便下文的使用。  
  43.         }  
  44.     }  
  45. }  
  46.   
  47. int main()  
  48. {  
  49.     while(~scanf("%d%d",&n,&m))  
  50.     {  
  51.         if(!n&&!m)                  //注意题目给的数据,考虑边界  
  52.         {  
  53.             printf("0\n");  
  54.             continue;  
  55.         }  
  56.         init();  
  57.         char c;  
  58.         memset(dp,0,sizeof(dp));  
  59.         memset(ma,0,sizeof(ma));  
  60.         for(int i=0;i<n;i++)             //存图。‘H’为1 ,‘P'为0  
  61.         {  
  62.             for(int j=0;j<m;j++)  
  63.             {  
  64.                cin>>c;  
  65.                if(c=='H')                    
  66.                 ma[i]=ma[i]|(1<<j);  
  67.             }  
  68.         }  
  69.   
  70.         int now=1;                     //滚动数组的初始化  
  71.         int ans=0;  
  72.   
  73.         for(int i=0;i<ns;i++)          //枚举第一行,确定第一行的状态。  
  74.         {  
  75.             if(st[i]>=(1<<m)) break;  
  76.             if(ma[0]&st[i]) continue;  
  77.   
  78.             dp[now][i][0]=num[i];  
  79.             ans=max(ans,dp[now][i][0]);  
  80.         }  
  81.         if(n==1)  
  82.         {  
  83.             printf("%d\n",ans);  
  84.             continue;  
  85.         }  
  86.         now^=1;                       //滚动数组的变化,不要忘了。滚动变化,注意连起来理解。  
  87.       
  88.         for(int i=0;i<ns;i++)         //枚举第二行的状态,确定初始的两行的状态,以便后面的不断更新。  
  89.         {  
  90.             if(st[i]>=(1<<m)) break;  
  91.             if(ma[1]&st[i]) continue;  
  92.   
  93.             for(int j=0;j<ns;j++)  
  94.             {  
  95.                 if(st[j] >= (1<<m)) break;  
  96.                 if(ma[0]&st[j]) continue;  
  97.   
  98.                 if(!(st[i]&st[j]))  
  99.                 {  
  100.                     dp[now][i][j]=max(dp[now][i][j],dp[now^1][j][0]+num[i]);  
  101.                     ans=max(ans,dp[now][i][j]);  
  102.                 }  
  103.             }  
  104.         }  
  105.         if(n==2)  
  106.         {  
  107.             printf("%d\n",ans);  
  108.             continue;  
  109.         }  
  110.         now^=1;  
  111.   
  112.         for(int x=2;x<n;x++)              //从第3行开始。注意是行数。  
  113.         {  
  114.             for(int i=0;i< ns;i++)          //开始枚举后面每一行的状态。 x表示行数,i表示当前状态,j表示上一行状态,k表示上上一行的状态。  
  115.             {  
  116.                 if(st[i]>=(1<<m)) break;  
  117.                 if(ma[x]&st[i]) continue;  
  118.   
  119.                 for(int j=0;j<ns;j++)  
  120.                 {  
  121.                     if(st[j]>=(1<<m)) break;  
  122.                     if(ma[x-1]&st[j]) continue;  
  123.                     if(st[i]&st[j])  continue;  
  124.   
  125.                     for(int k=0;k<ns;k++)  
  126.                     {  
  127.                         if(st[k]>=(1<<m)) break;  
  128.                         if(ma[x-2]&st[k]) continue;  
  129.                         if(st[i]&st[k] || st[j]&st[k]) continue;  
  130.   
  131.                         dp[now][i][j]=max(dp[now][i][j],dp[now^1][j][k]+num[i]);  
  132.                         ans=max(ans,dp[now][i][j]);  
  133.                     }  
  134.                 }  
  135.             }  
  136.             now^=1;  
  137.         }  
  138.         printf("%d\n",ans);  
  139.     }  
  140.     return 0;  
  141. }  
0 0
原创粉丝点击