POJ1185 炮兵阵地 (状态压缩DP)

题目点我点我点我
题目大意:简单不多说

思路:先全列出所有可行的状态,放炮兵就为1,不放就为0,这时先不考虑地形,把可行状态列出来后,再与地形匹配,山地H为1,平原p为0,这样两者以二进制匹配得数为0则匹配成功。
然后第i行(i>2)只由第i-1行的状态和第i-2行的状态决定,dp[i][j][k]代表第i行的状态为j,第i-1行状态为k,所以有状态转移方程 dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+soldier[j]),soldier[j]表示j状态下炮兵数量。
另外要初始化第一行第二行的状态,详情见代码。

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define maxn 60  //状态数,另用程序跑跑记录10列是有60种符合的状态
int state[maxn],soldier[maxn];    //state记录可行状态,soldier记录士兵数
int top,N,M;   //top记录最多的可行状态数
int place[102],dp[102][maxn][maxn];  //place记录输入地形
char s[11];

void init()  //初始化所有可行状态,1代表放士兵,0则无
{
    top=0;
    for(int i=0;i<1<<10;i++)
    {
        if(((i<<1)&i)||((i<<2)&i))   //左边两格是否有其他炮兵
            continue;
        int temp=i;
        while(temp)
        {
            if(temp&1)soldier[top]++;   //每一个1炮兵就多一个
            temp>>=1;              
        }
        state[top++]=i;    //记录这个状态
    }

}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    init();
    while(~scanf("%d%d",&N,&M))
    {
        int ans=0;
        for(int i=1;i<=N;i++)
        {
            scanf("%s",s);
            place[i]=0;
            for(int j=0;j<M;j++)
            {
                place[i]<<=1;
                if(s[j]=='H')    //山地就二进制记为1
                    place[i]++; 
            }
        }
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=N;i++)
        {
            if(i==1)   //初始化第一行
            {
                for(int j=0;j<top&&state[j]<(1<<M);j++)
                {
                    if((state[j]&place[i])==0)   //可行状态与地形匹配
                        dp[1][j][0]=soldier[j];
                    ans=max(ans,dp[1][j][0]);
                }
            }
            else if(i==2)   //初始化第二行
            {
                for(int j=0;j<top&&state[j]<(1<<M);j++)
                {
                    if( (state[j]&place[i]) == 0 )  
                    {
                        for(int k=0;k<top&&state[k]<(1<<M);k++)
                        {
                            if( ( (state[k]&place[i-1]) ==0 ) && ( (state[k]&state[j]) ==0))  
                                //可行状态与地形匹配以及第i行可行状态与第i-1行可行状态匹配,下面也一样
                                dp[2][j][k]=dp[1][k][0]+soldier[j];  //这里是第二行,直接加,不用max函数
                            ans=max(ans,dp[2][j][k]);
                        }
                    }
                }
            }
            else
            {
                for(int j=0;j<top&&state[j]<(1<<M);j++)
                {
                    if( (state[j]&place[i]) ==0)
                    {
                        for(int k=0;k<top&&state[k]<(1<<M);k++)
                        {
                            if(( (state[k]&place[i-1]) ==0)&&( (state[k]&state[j]) ==0))
                            {
                                for(int l=0;l<top&&state[l]<(1<<M);l++)
                                {
                                    if(( (state[l]&place[i-2] )==0)&&( (state[l]&state[j]) ==0)&&( (state[l]&state[k]) ==0))
                                    {
                                        dp[i][j][k]=max(dp[i][j][k],dp[i-1][k][l]+soldier[j]);
                                    }
                                }
                            }
                            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、付费专栏及课程。

余额充值