POJ 1185 炮兵阵地

感觉上很难,确实自己做一开始没有想到是dp问题,同时没有进行剪枝,同时有一些准备工作没有做好

  • 没有提前将每一行的信息转成数字信息(做题经验不足)
  • 没有提前把每行可能的情况抽取出来
  • 没有想到其实确实是一个dp问题(其实一开始的贪心思路有些不太对)

判断贪心和dp问题的一个方法可以是看数据范围

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int dp[105][65][65];
int cnt=0;
int str[65];
int sum[65];
int map[110];
char s[110][110];
bool check(int x)//判断这个排列是否为普通的合法序列
{
    if(x&(x<<1)) return false;
    if(x&(x<<2)) return false;
    return true;
}
int getSum(int x)//当前状态的炮兵数目
{
    int ans=0;
    while(x>0)
    {
        if(x&1) ans++;
        x>>=1;
    }
    return ans;
}
void before_hand(int mid)//预处理出所有的可能合法炮兵状态,cnt统计状态数
{
    for(int i=0;i<(1<<mid);i++)
    {
        if(check(i))
        {
            str[cnt]=i;//str数组记录状态,dp中的j和k都是str数组的下标
            sum[cnt]=getSum(i);
            cnt++;
        }
    }
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    memset(dp,-1,sizeof dp);
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            char a;
            cin >> a;
            if(a=='H') map[i]|=(1<<j);//统计每一行的不合法格子状态
        }
    }
    before_hand(m);//其实可以不传参
    for(int i=0;i<cnt;i++)
    {
        if(!(str[i]&map[0])) dp[0][0][i]=sum[i];//先处理出第一行的情况
    }
    for(int r=1;r<n;r++)//枚举行数
    {
        for(int i=0;i<cnt;i++)//枚举当前行的排列
        {
            if(str[i]&map[r]) continue;
            for(int j=0;j<cnt;j++)//枚举上一行的排列情况
            {
                if(str[i]&str[j]) continue;
                for(int k=0;k<cnt;k++)//枚举i-2行的排列情况
                {
                    if(str[i]&str[k]) continue;
                    if(dp[r-1][k][j]==-1) continue;
                    dp[r][j][i]=max(dp[r][j][i],dp[r-1][k][j]+sum[i]);//更新即可
                }
            }
        }
    }
    int ans=0;//统计答案
    for(int i=0;i<cnt;i++)
    {
        for(int j=0;j<cnt;j++)
        {
            ans=max(ans,dp[n-1][i][j]);
        }
    }
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2023-01-27 18:29  ymx10086  阅读(40)  评论(0)    收藏  举报