状压dp NOI 2001 炮兵阵地

17 篇文章 0 订阅
6 篇文章 0 订阅

题意:
给你一个 n ∗ m n∗m nm的网格,每个各自最多放一个炮兵,每一个格子有地形,平原可以放,山地不能放,并且一个炮兵的上下左右两个内不能放炮兵,问最多放多少炮兵。 n ≤ 100 ; m ≤ 10 n≤100;m≤10 n100m10

可以发现m非常小,考虑状压dp,这道题与常规状压dp的不同点是这道题的每一行状态在转移时要看之前两行的状态。

首先我们先将每一行哪些格子可以放用二进制表示出来, g [ i ] g[i] g[i]表示第 i i i行地形情况的二进制状态。然后处理出一行内所有合法的情况。我们设 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示第 i i i行,当前状态的 j j j,上一行的状态是 k k k最多放多少炮兵,我们按顺序枚举当前行,上一行,和上两行的状态,要保证当前行的状态不与当前行的地形状态冲突,当前行的状态不与上一行和上两行的状态冲突,上一行和上两行的状态不冲突。

所以 d p [ i ] [ j ] [ k ] = m a x ( d p [ i ] [ j ] [ k ] , d p [ i − 1 ] [ k ] [ q ] + s [ j ] ) dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][q]+s[j]) dp[i][j][k]=max(dp[i][j][k]dp[i1][k][q]+s[j]) s [ j ] s[j] s[j] j j j状态下 1 1 1的个数,即为该状态下可以放的炮兵的个数。最后答案就是 m a x max max{ d p [ n ] [ i ] [ j ] dp[n][i][j] dp[n][i][j]}, i , j i,j i,j为最后一行和倒数第二行的所有状态。

#include<bits/stdc++.h>
using namespace std;
int n,m,g[1000000],num,res[1000000],ans,dp[105][1024][1024],s[1000000];
char st[1001000];
//g数组为第i行山地平原情况的状压,0为平原,1为山地 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",st+1);
        for(int j=1;j<=m;++j)
        {
            if(st[j]=='H')
                g[i]+=1<<(j-1);
        }		
    }	
    for(int i=0;i<(1<<m);++i) 
    {
        if(!(i&(i>>1))&&!(i&(i>>2))&&!(i&(i<<1))&&!(i&(i<<2)))
        {
            res[++num]=i;//可行状态 
            int w=i;
            while(w)
            {
                if(w%2==1)
                    s[num]++;//该状态下1的个数(放的炮的个数) 
                w/=2;
            }
            if(!(i&(g[1])))//初始化第一行状态 
                dp[1][num][0]=s[num]; 
        }	
    }
    
    for(int i=1;i<=num;++i)//枚举第一行的状态 
        for(int j=1;j<=num;++j)//枚举第二行的状态 
            if(!(res[i]&res[j])&&!(g[2]&res[j]))//判断是否与地形和第一行冲突
                dp[2][j][i]=max(dp[2][j][i],dp[1][i][0]+s[j]);
                
    for(int i=3;i<=n;++i)
        for(int j=1;j<=num;++j)//当前行的状态 
            if(!(g[i]&res[j]))//当前行的状态不与地形冲突 
                for(int k=1;k<=num;++k)//前一行的状态 
                    if(!(res[k]&res[j]))//前一行的状态与当前行不冲突 
                        for(int q=1;q<=num;++q)//前两行的状态 
                            if(!(res[q]&res[k])&&!(res[q]&res[j]))//前两行的状态不与前一行的状态和当前行的状态冲突 
                                dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][q]+s[j]);
                                
    for(int i=1;i<=num;++i)
        for(int j=1;j<=num;++j)
            ans=max(ans,dp[n][i][j]);
            
    cout<<ans;					
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值