【NOI2001】炮兵阵地

题意

给你一张有N*M的平原和山地的图,只能在平原上放置炮兵,炮兵的攻击范围如图所示,求最多能放多少个炮兵?

数据规模:N <= 100;M <= 10。

分析

此题比起入门级的状压,难在了状态更多,影响的也不只一行了,而是两行。

所以我们需要加维+减状态

dp[i][j][k],扫到i行为止,当前行的状态为j,上一行的状态为k的方案最大值。

如果再把山地的算进去,状态就太多了,需要预处理剪掉一些。

而相应的,预处理也要预处理1,2两行,枚举第i行时也必须枚举i-1行,i-2行。

减状态的方法:映射。用cnt表示可行状态编号,id[ ]数组对应了这个状态

还需要预处理出每个状态可以放置多少炮兵,便于统计答案

注意好好算一下数组开多大,一念之差很容易RE or MLE

#include<bits/stdc++.h>
using namespace std;
#define N 110
#define ll long long

ll ans=0,dp[N][N][N],num[N];
int n,m,mx,cnt;
int e[N][N],ok[N],okk[N][N],id[N];
int mp[N];
string s;

int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>s;
        for(int j=1;j<=m;j++)
            if(s[j-1]=='P')e[i][j]=1;
    }
    mx=(1<<m)-1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            mp[i]=(mp[i]<<1)+e[i][j];
    
    for(int i=0;i<=mx;i++)
    {
        if( (((i<<1)&i)==0) & (((i<<2)&i)==0) )
        {
            id[++cnt]=i;
            ok[cnt]=1;
            int pos=i;
            while(pos)
            {
                if(pos&1)num[cnt]++;
                pos>>=1;
            }
        }
    }    
    
    for(int i=1;i<=cnt;i++)
        if(ok[i] & ((id[i]&mp[1])==id[i]))
        {
            okk[1][i]=1;
            dp[1][i][0]=num[i];
        }    
    
    for(int i=1;i<=cnt;i++)
    {
        if(ok[i] & ((id[i]&mp[2])==id[i]))
        {
            okk[2][i]=1;
            for(int j=1;j<=cnt;j++)
            {
                if(!okk[1][j])continue;
                if( (id[i]&id[j])==0 )
                    dp[2][i][j]=max(dp[2][i][j],dp[1][j][0]+num[i]);
            }
        }
    }    
    
    for(int i=3;i<=n;i++)
        for(int j=1;j<=cnt;j++)
        {
            if(ok[j] & ((id[j]&mp[i])==id[j]))
            {
                okk[i][j]=1;
                for(int k=1;k<=cnt;k++)
                {
                    if(!okk[i-2][k])continue;
                    if( (id[k]&id[j]) )continue;
                    for(int t=1;t<=cnt;t++)
                    {
                        if(!okk[i-1][t])continue;
                        if(((id[k]&id[t])==0) & ((id[t]&id[j])==0))
                            dp[i][j][t]=max(dp[i][j][t],dp[i-1][t][k]+num[j]);
                    }
                }
            }
        }
        
    for(int i=1;i<=cnt;i++)
        for(int j=1;j<=cnt;j++)
            ans=max(ans,dp[n][i][j]);    
    cout<<ans;
    return 0;    
        
}

 

posted @ 2018-08-22 21:39  WJEMail  阅读(219)  评论(0编辑  收藏  举报