poj 1185 炮兵阵地

dede说这是一道很经典的题。

 

炮兵阵地
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 14887   Accepted: 5585

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 4
PHPP
PPHH
PPPP
PHPP
PHHP

Sample Output

6

————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

刚才poj上贴了代码过了,自己还没实现过。

据说是状态压缩?(啥意思= =)

首先要知道的我认为“压缩”的关键就是用二进制来表示和用位运算判断一种情况是否合法:

用1来表示当前位置是山(H)或在炮兵的攻击范围内,用0表示当前位置可以布置炮兵(是平原并且不在别的炮兵攻击范围里)然后将这一行看做一个二进制数。(下文中的m,n,k1,k2,k'就是十进制形式)

总体思路1.预处理出地图的状态保存的数组h[i]中。

     2.由于x[i][j]是否能放炮兵是由它上下左右2格范围所决定的,故①左右只要满足二进制表示的数1所在的位置两格内没有别的1。②[i-1][j],[i-2][j]不为1。就是合法的。

     3.状态转移方程dp[i+1][k'][k1]=dp[i][k1][k2]+n(k')

dp[i][m][n]表示在i-1行是状态m,i-2行是状态n下前i行所放的炮兵数。n(k')表示k'转化为二进制后有多少个1。

具体实现1.建立一个函数用于枚举行的状态。判断同行的炮兵之间是否相互攻击((x<<1)& x)|(x<<2)& x),把每行的合法状态保存到一个数组cnt[]里。

     2.开始循环枚举。

      {

        枚举每一行i

        {

            枚举i行的可能性

            {

                枚举i-1行的可能性

                {

                  判断i 与i-1行是否冲突

                }

                枚举i-2行的可能性

                {

                   判断i 与i-1行是否冲突

                }

            }

      3.找dp[n][k1][k2]中最大的即为答案。k1,k2∈cnt[]。

 

 1 #include<stdio.h>
 2 #include<string.h>
 3 int cnt,m,n;
 4 int dp[100][64][64],num[64],state[64],bitmap[100];
 5 void init()
 6 {
 7     int tmp;
 8     cnt=0;
 9     for(int i=0;i<(1<<m);i++)  //枚举每行的状态,从0到2^m - 1,判断其是否合法
10     {
11         tmp=i;
12         if( ((tmp<<1)&i) | ((tmp<<2)&i) ) continue;//判断该行在这个状态时是否合法(任意炮兵都不在其他炮兵的攻击范围之内)
13         state[cnt]=i;   //通过数组state[]记录合法的状态(十进制表示)
14         num[cnt]=tmp&1;  //num[]数组记录这个合法状态下‘1’的个数(也就时炮兵的个数)
15         while( tmp = (tmp>>1) )
16             num[cnt]+=tmp&1;
17         cnt++;             //此函数的统计是假设当该行都为平地时,即共有cnt+1个合法状态
18     }
19 }
20 void solve()
21 {
22     int ans,i,j,k,p;
23     memset(dp,0,sizeof(dp));
24     for(i=0;i<n;i++)                         //枚举每一行
25         for(j=0;j<cnt;j++)                  //先枚举第i行的可能状态
26         {
27             if(bitmap[i]&state[j]) continue;  //地图中标记为“山地”的点不能布兵
28             if(i==0)  dp[i][j][0]=num[j];
29             else if(i==1)
30             {
31                 for(k=0;k<cnt;k++)                  //枚举第i-1行的可能状态
32                 {
33                     if(bitmap[i-1]&state[k]) continue;  //判断第i-1行的k状态是否和山地冲突,冲突就跳到下一个状态k+1
34                     if(state[j]&state[k]) continue;    //判断上下两行(i-1和i)的合法状态是否兼容彼此
35                     if(dp[i][j][k]<dp[i-1][k][0]+num[j])
36                         dp[i][j][k]=dp[i-1][k][0]+num[j];
37                 }
38             }
39             else
40             {
41                 for(k=0;k<cnt;k++)                 //枚举第i-1行的可能状态
42                 {
43                     if(bitmap[i-1]&state[k]) continue;   //判断第i-1行的k状态是否和山地冲突,冲突就跳到下一个状态k+1
44                     if(state[j]&state[k]) continue;        //判断上下两行(i-1和i)的合法状态是否兼容彼此
45                     for(p=0;p<cnt;p++)                 //枚举第i-2行的可能状态
46                     {
47                         if(bitmap[i-2]&state[p]) continue; //判断第i-2行的p状态是否和山地冲突,冲突就跳到下一个状态p+1
48                         if(state[k]&state[p] || state[j]&state[p]) continue;//判断上下两行(i-2和i-1,i-2和i)的合法状态是否兼容彼此
49                         if(dp[i][j][k]<dp[i-1][k][p]+num[j])
50                             dp[i][j][k]=dp[i-1][k][p]+num[j];
51                     }
52                 }
53             }
54         }
55 
56   ans=0;
57     //for(i=0;i<n;i++)
58         for(j=0;j<cnt;j++)
59             for(k=0;k<cnt;k++)
60                 if(dp[n-1][j][k]>ans)
61                     ans=dp[n-1][j][k];
62     printf("%d\n",ans);
63 }
64 int main()
65 {
66     char s[12];
67  int i,j;
68     while(scanf("%d%d",&n,&m)!=EOF)
69  {
70      memset(bitmap,0,sizeof(bitmap));
71   for(i=0;i<n;i++)
72   {
73    scanf("%s",s);
74             for(j=0;j<m;j++)if(s[j]=='H')bitmap[i]+=(1<<(m-1-j));
75     //if(s[j]=='H')bitmap[i]|=(1<<j);
76   }
77         init();
78         solve();
79  }
80  return 0;
81 
82 }
View Code

 

 

 

 

 

 

 

 

posted @ 2013-05-26 15:19  蛋丁  阅读(277)  评论(3编辑  收藏  举报