POJ 1185 解题报告 炮兵阵地



题目是中文的,我就不描述题意了。
题目用到的主要算法是状态压缩dp。

思路是,我们要知道n行最多的炮数,只要知道n-2行所有状态最多的炮数,就可以根据n-1行和n行最多可行的状态算出。也就是说,n-2行以前的炮无论怎样放,都不会影响到第n行炮的放法。
  1
  2#include <cstdio>
  3#include <cmath>
  4#include <cstdlib>
  5
  6const int large = (int)pow( 210 );
  7int n,m;
  8char map[105][15];
  9int dp[60][60][101];
 10int stack[large];
 11int len = 0;
 12
 13//now是当前行,last是上一行,n是行号。其中,now,last都是stack的下标,不是状态
 14int Fun( int now, int last, int n )
 15{
 16    int max = 0;
 17    int s = 0;
 18    int tmp = stack[now]|stack[last]; //已放了炮的列
 19    if ( dp[now][last][n] != -1 ) return dp[now][last][n];
 20    //统计now状态放了多少炮
 21    for ( int i = 0; i < m; ++i )
 22    {
 23        if ( (stack[now]&(~(1<<i))) != stack[now] ) ++s;
 24    }

 25    if ( n == 1 ) //结束条件,第一行则返回此行的炮数
 26    {
 27         dp[now][last][1= s;
 28         return s;
 29    }

 30    for ( int i = 0; i < len; ++i )
 31    {
 32        int flag = 1;
 33        int s = 0;
 34        if ( tmp&stack[i] ) continue//若相互冲突则不算
 35        //若此状态的炮兵放在了山地也不算
 36        for ( int j = 0; j < m; ++j ) 
 37        {
 38            if ( map[n-2][j] == 'H' && (stack[i]&(~(1<<j))) != stack[i] )
 39            {
 40                     flag = 0;
 41                     break;
 42            }

 43        }

 44        if ( flag ) max >?= Fun( last, i, n-1 ); //从所有状态里挑最大的保存
 45    }

 46    max += s; //再加上now这行的炮数
 47    dp[now][last][n] = max;
 48    return max;
 49}

 50
 51//此函数用来判断是不是超出范围
 52bool OK( int a )
 53{
 54     if ( a<0 || a>= m ) return false;
 55     else return true;
 56}

 57
 58int main()
 59{
 60    int power;
 61    int max = 0;
 62    scanf( "%d%d"&n, &m );
 63    power = (int)pow( 2, m );
 64    
 65    //第0行初始化为山地
 66    for ( int i = 0; i < m; ++i ) map[0][i] = 'H';
 67    
 68    //读入数据储存到map中
 69    for ( int i = 1; i <= n; ++i )
 70    {
 71        scanf( "%s", map[i] );
 72    }

 73
 74    /*在0~power这么多状态中,绝大部分状态是可以舍弃的,因为相邻和隔一个的位置不能同时放炮。我们先把不能出现的状态剔除,将可能出现的状态储存在stack数组里,可以节约时间和空间。*/
 75    //初始化stack,test OK 
 76    for ( int i = 0; i < power; ++i )
 77    {
 78        int flag = 0;
 79        for ( int j = 0; j < m; ++j )
 80        {
 81            if ( (i&(~(1<<j))) != i )//如果i的j位是1,也就是放炮兵部队 
 82            {
 83                 if ( OK(j-1&& (i&(~(1<<(j-1))))!=i ) //j-1位合法且放了炮兵,则不合要求 
 84                 {
 85                      flag = 1
 86                      break;
 87                 }

 88                 if ( OK(j-2&& (i&(~(1<<(j-2))))!=i ) //j-1位合法且放了炮兵,则不合要求 
 89                 {
 90                      flag = 1
 91                      break;
 92                 }

 93                 if ( OK(j+1&& (i&(~(1<<(j+1))))!=i ) //j-1位合法且放了炮兵,则不合要求 
 94                 {
 95                      flag = 1
 96                      break;
 97                 }

 98                 if ( OK(j+2&& (i&(~(1<<(j+2))))!=i ) //j-1位合法且放了炮兵,则不合要求 
 99                 {
100                      flag = 1
101                      break;
102                 }

103            }
 
104        }

105        if ( flag == 0 )
106        {
107             stack[len++= i;
108        }

109    }

110    
111    //初始化dp数组
112    for ( int i = 0; i < 60++i )
113    {
114        for ( int j = 0; j < 60++j )
115        {
116            for ( int k = 0; k < 101++k )
117            {
118                dp[i][j][k] = -1;
119            }

120        }

121    }

122
123    //取遍所有本行和相邻的上一行的所有状态,取最大保留。并在传参前确保传入的参数是合法的。
124    for ( int i = 0; i < len; ++i )
125    {
126        for ( int j = 0; j < len; ++j )
127        {
128            int flag = 1;
129            if ( (stack[i]&stack[j]) ) continue//如果同一列同时有炮则舍弃
130            for ( int k = 0; k < m; ++k )//剔除不合法的情况 
131            {
132                if ( map[n][k] == 'H' && (stack[i]&(~(1<<k)))!=stack[i] ) //此状态在山地方了炮
133                {
134                     flag = 0;
135                     break;
136                }

137                if ( map[n-1][k] == 'H' && (stack[j]&(~(1<<k)))!=stack[j] )
138                {
139                     flag = 0;
140                     break;
141                }

142            }

143            if ( flag ) max >?= Fun( i, j, n ); //取最大存在max里
144        }

145    }

146    printf( "%d\n", max );
147   
148    return 0;
149}

150



posted @ 2008-07-18 12:17  王婷婷๑→‿ฺ←๑  阅读(3714)  评论(1编辑  收藏  举报