HDU 3811 炮兵阵地

题意:

中文题

http://poj.org/problem?id=1185

算法:

N <= 100;M <= 10。

枚举每行状态,1表示该行放炮兵,0表示不放,相邻两位不能有1,这样的状态才合法

特殊处理第一行,第二行状态。

int cow[110];//保存行状态信息
int num[100]; //保存状态数
int sum[100]; //保存状态对应的1,即炮兵数
int dp[110][64][64]; //dp方程, dp[i][j][k]表示第i行状态为j,上一行状态为k

枚举后面的行。由前往后推,num[i]&num[j] == 0 num[i]&num[k] == 0 num[j]&num[k] == 0

View Code
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int cow[110];//保存行状态信息 
int num[100]; //保存状态数
int sum[100]; //保存状态对应的1,即炮兵数
int dp[110][64][64]; //dp方程, dp[i][j][k]表示第i行状态为j,上一行状态为k
int N, M, len; //N行M列 
//预处理,求出所有状态 
void pre( )
{
  len = 1;
  for( int i = 0; i < (1<<M); i++)
  {
     int ans = i;
     if( (i&(ans<<1)) || (i&(ans<<2))) //使其合法 
       continue;
      num[len] = i;
      sum[len] = ans&1;
      while( ans )
      {
         ans >>= 1;
         sum[len] += ans&1;       
      } 
      len++; 
  }        
}

//判断状态是否合法,即状态为1的地方,在地图上应该为平原,如果状态为1的地方,地图上为山地则返回false
bool jugde( int state, int mp)
{
  for( int i = 1; i < ( 1 << M); i = i << 1)
  {
     if( (state & i) && ((mp & i) == 0) )
        return false;      
  }    
  return true;   
}

 
int main( )
{
  char str[110][20];
  while( scanf("%d%d", &N, &M) != EOF)
  { 
     pre( );
     for( int i = 1; i <= N; i++)
         scanf("%s",&str[i]);
     memset(cow, 0,sizeof(cow));
     for( int i = 1; i <= N; i++)
     {
          for( int j = 0; j < M; j++)
          {
             if( str[i][j] == 'P' )
                 cow[i] += (1<<j);
          }  
     }
     memset(dp, 0, sizeof(dp));
     //处理第一行 
     for( int i = 1; i < len; i++)
     {
        if( jugde(num[i], cow[1]) )
        {
           for( int j = 1; j < len; j++)
                dp[1][i][j] = sum[i];
        }     
          
     }
     //处理第二行
     for( int i = 1; i < len; i++)
     {  
       if( jugde(num[i], cow[2]) )
       {
        for( int j = 1; j < len; j++)
        {   
               for( int k = 1; k < len; k++)
               {
                  if( ( (num[i]&num[j]) == 0 ) && dp[1][j][k] + sum[i] > dp[2][i][j] )
                      dp[2][i][j] = dp[1][j][k] + sum[i];
                    
               }      
        }
       }          
     }
     //对其后面的若干行,状态转移
     for( int n = 3; n <= N; n++)
     for( int i = 1; i < len; i++)
       for( int j = 1; j < len; j++)
       {
         if( jugde(num[i],cow[n]) )
         {
           for( int k = 1;k < len; k++)
           {
                if( ((num[i]&num[j]) == 0 ) && ((num[j]&num[k]) == 0 ) && ((num[i]&num[k]) == 0 ))
                {
                    
                    if( dp[n][i][j] < dp[n-1][j][k] + sum[i] )
                        dp[n][i][j] = dp[n-1][j][k] + sum[i];     
                }       
           }
         }
       }
     int ans = 0;
     for( int i = 1; i < len; i++)
        for( int j = 1; j < len; j++)       
           if( dp[N][i][j] > ans)
             ans = dp[N][i][j];
     printf("%d\n",ans);
  }
  return 0;    
}

总结下状态压缩算法:

0.判断该题是否可以用状态压缩,

1.选择表示表示状态方法,含义,2进制,3进制,。。。。。定义DP方程,含义

2。根据题意,预处理合法状态

3.特殊处理边界条件

4.从前往后推,枚举所有状态,满足相应题目要求

5.数据量大的要用矩阵优化,记忆化搜索

6.输出最优解

 

转载于:https://www.cnblogs.com/tangcong/archive/2012/08/06/2625371.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值