poj 1185(状态压缩dp)

本文通过动态规划方法解决了一种特殊的放大炮兵布局问题,重点在于利用三维数组进行状态表示与转移,确保每行炮兵布局符合硬件条件且避免炮兵间的互相攻击。
/*参考博客   http://blog.youkuaiyun.com/accry/article/details/6607703
【解析】可以发现,对于每一行放大炮的状态,只与它上面一行和上上一行的状态有关,每一行用状态压缩的表示方法,0表示不放大炮,1表示放大炮,同样的,先要满足硬件条件,即有的地方不能放大炮,然后就是每一行中不能有两个1的距离小于2(保证横着不互相攻击),这些要预先处理一下。然后就是状态表示和转移的问题了,因为是和前两行的状态有关,所以要开个三维的数组来表示状态,当前行的状态可由前两行的状态转移而来。即如果当前行的状态符合前两行的约束条件(不和前两行的大炮互相攻击),则当前行的最大值就是上一个状态的值加上当前状态中1的个数(当前行放大炮的个数) 

【状态表示】dp[i][j][k] 表示第i行状态为k,第i-1状态为j时的最大炮兵个数。 

【状态转移方程】dp[i][k][t] =max(dp[i][k][t],dp[i-1][j][k]+num[t]); num[t]为t状态中1的个数 

【DP边界条件】dp[1][1][i] =num[i] 状态i能够满足第一行的硬件条件(注意:这里的i指的是第i个状态,不是一个二进制数,开一个数组保存二进制状态) 

*/ 

#include <stdio.h>
#include <string.h>

int n,m;

char map[110][20];
int num[110],top;
int dp[110][70][70];
int stk[70],cur[70];

 
int max(int x,int y)
{
   if(x>y) return x;
   return y;    
}

/*
  判断x是否合法,不合法(相邻的两个1之间的距离小于3) 
*/
bool isok(int x)
{
   if(x&(x<<1)) return 0;
   if(x&(x<<2)) return 0;
   return 1;     
}


// 找到所有合法的状态,存入stk数组 
void init()
{
    top=0;
   for(int i=0;i<(1<<n);i++)
       if(isok(i))
       stk[++top]=i;
}


//判断状态x是否与第k行匹配  
bool match(int x,int k)
{
  if(cur[k]&x) return 0;
    
  return 1;  
}

// 计算状态x中1的个数
 
int Count(int x)
{
   int  count=0;
   while(x)
   {
     count++;
     x=x&(x-1);
   }  
    return count;
}


int main()
{
  
   while(scanf("%d%d",&m,&n)!=EOF)
   {
        if(n==0 && m==0)  break;
        
        init();
                                   
        for(int i=1;i<=m;i++)
          scanf("%s",map[i]+1);
           
        for(int i=1;i<=m;i++)
        {
           cur[i]=0;
           for(int j=1;j<=n;j++)
           {
               if(map[i][j]=='H')
                cur[i]+=(1<<(j-1));
           } 
        }
        
        
        
        memset(dp,-1,sizeof(dp));
        
        //初始化第一行状态 
        for(int i=1;i<=top;i++)
        {
            num[i] = Count(stk[i]);
            if(match(stk[i],1))
             dp[1][1][i]=num[i];
        }
        
        
        for(int i=2;i<=m;i++)
            for(int t=1;t<=top;t++)  //t 当前状态 
            {
               if(!match(stk[t],i))continue;
               
               for(int j=1;j<=top;j++)    //j 上上个状态 
               {
                  if(stk[t]&stk[j]) continue;
                  
                  for(int k=1;k<=top;k++)  //k代表上一个状态 
                  {
                     if(stk[t]&stk[k])  continue;  //k状态不可以到t状态 
                     if(dp[i-1][j][k]==-1)  continue;
                     
                     dp[i][k][t] = max(dp[i][k][t],dp[i-1][j][k]+num[t]);
                  }                  
                                  
               }        
            }
            
       int ans=0;  
       for(int i=1; i<=m; i++)  
          for(int j=1; j<=top; j++)  
            for(int k=1; k<=top; k++)  
              ans = max(ans,dp[i][j][k]);  
       printf("%d\n",ans);    
  }
  
     
    
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值