POJ 1185 炮兵阵地 (状态压缩dp)

这题的状态我是这样定义的:
dp[i][j][k] 表示在第k行前,当前状态是i,上一行的状态是j的时候,最多可以放多少个炮。

一开始我定义的时候,没有中间那个j,但是后来想想似乎不对。
如果这样定义的话,碰到这种情况就不对了。

HPH
PHP
HPH

第三行,按照(错误)定义,是从第二行来的,第二行是从第一行来。
但是这时的第三行和第一行明显不可以同时存在于第二列上。
这样的定义就会算进去,会错误。

所以需要多加一维。

另外这题i,j并不是真正的状态,而是状态集合的下标。
因为通过运算,发现,一行最多只有60种可行情况。

所以数组不需要开到1<<10这样大,只要开比60多就行了。

代码如下:

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
using namespace std;

char grid[120][12];
int able[100][120];
int pt[120];
int dp[100][100][120];
int n,m;

void init()
{
    memset(grid,0,sizeof(grid));
    memset(able,0,sizeof(able));
    memset(pt,0,sizeof(pt));
    memset(dp,0,sizeof(dp));
}

bool test1(int now,int row)
{
    for(int i=m-1;i>=0;i--)
    {
        if(now%2==1 && grid[row][i]=='H') return false;
        now=now/2;
    }
    return true;
}

int test2(int S)
{
    return (((S<<1)&S) | ((S<<2)&S));
}

int bitcount(int S)
{
    int cnt=0;
    /*while(S>0)
    {
        cnt+=(S&1);
        S=(S>>1);
    }*/
    while(S>0)
    {
        if(S%2==1) cnt++;
        S=S/2;
    }
    return cnt;
}

int main()
{
    while(cin>>n>>m)
    {
        init();
        for(int i=0;i<n;i++)
        {
            scanf("%s",grid[i]);
        }

        for(int i=0;i<n;i++)
        {
            for(int S=0;S<(1<<m);S++)
            {
                if(test1(S,i)==false) continue;
                if(test2(S)!=0) continue;
                able[pt[i]][i]=S;
                pt[i]++;
            }
        }
        //printf("%d\n",pt[0]);

        //枚举第一行
        for(int i=0;i<pt[0];i++)
        {
            int S=able[i][0];
            dp[i][0][0]=bitcount(S);
        }

        //枚举第二行
        for(int i=0;i<pt[1];i++)
        {
            int S=able[i][1];
            for(int j=0;j<pt[0];j++)
            {
                int P=able[j][0];
                if( (P&S)!=0 ) continue;
                dp[i][j][1]=max(dp[i][j][1],dp[j][0][0]+bitcount(S));
            }
        }

        //正式开始
        for(int r=2;r<n;r++)
        {
            for(int i=0;i<pt[r];i++)
            {
                int S=able[i][r];
                for(int j=0;j<pt[r-1];j++)
                {
                    int P=able[j][r-1];
                    if((P&S)!=0) continue;
                    for(int k=0;k<pt[r-2];k++)
                    {
                        int T=able[k][r-2];
                        if((T&S)!=0 || (P&T)!=0) continue;
                        dp[i][j][r]=max(dp[i][j][r],dp[j][k][r-1]+bitcount(S));
                    }
                }
            }
        }

        int ans=0;
        for(int i=0;i<100;i++)
        {
            for(int j=0;j<100;j++)
            {
                ans=max(ans,dp[i][j][n-1]);
            }
        }
        printf("%d\n",ans);

    }


    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值