poj 1185 状态DP

本文介绍如何使用状态DP解决特定的炮兵布局问题,通过分析每行炮兵数与前两行的关系,实现最优解。利用状态压缩DP[k][a][b]表示第k行放炮兵的第a种状态与第k-1行放炮兵的第b种状态,通过状态转移实现最优布局。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

经典的状态DP啊,虽然也可以用最大点独立集来做。不严谨的话,这应该是我第一道状态DP题。

刚开始没有思路,不知道怎么设状态。看了网上一些思路,有点头绪了。

分析:对于第k行哪些点放炮兵使得1~k行炮兵数最多与第k-1,k-2行哪些点放炮兵有关,所以对于第k行需要前面两行的(k-1,k-2)的状态才能确定,

每个位置状态只有放与不放(0,1),所以想到用状态压缩dp[k][a][b]来表示,a表示第k行放炮兵的第a种状态,b表示第k-1行放炮兵的第b种状态。

为什么这样设状态?因为这样就可以进行状态转移了,如果设dp[k][a],则发现无法进行状态转移,此时常见的方法就是增加维度,即增加新的因素,更细致的描述状态。

dp[k][a][b]=max(dp[k][a][b],dp[k-1][b][p]+num[a]); 约束条件是 sta[a]&sta[b]==0, sta[a]&sta[p]==0(即k的第a种状态与k-1的第b种状态,k-2的第p种状态都不冲突。)

其中sta[a]是第k行所对应的第a个状态,num[a]为第a个状态对应的可以放炮兵的位置个数。

其他一些要说明的细节或者求法,在代码里。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 1<<7
int N,M,ans;
int dp[111][MAX][MAX];
int sta[MAX],num[MAX],sum,presta[MAX],prenum,prepresta[MAX],preprenum;
char ch[111][15];
void dfs(int i,int j,int p,int s)
{
    if(j>=M) {sta[++sum]=p;num[sum]=s;return ;}
    if(ch[i][j]=='P') dfs(i,j+3,p|(1<<j),s+1);
    dfs(i,j+1,p,s);
}
void DP()
{
    int i,j,k,p;
    prenum=preprenum=1;
    for(k=1;k<=N;k++)
    {
        sum=0;  //第k行对应的状态数。(即 有几个状态)
        memset(num,0,sizeof(num));  //用来存放 每个状态中1的个数(即可以放炮兵的位置数)
        dfs(k,0,0,0);    //求第k行对应的状态,以及每个状态可以放炮兵的个数。
        for(i=1;i<=sum;i++)
        {
            for(j=1;j<=prenum;j++)
            {
                for(p=1;p<=preprenum;p++)
                {
                    if(sta[i]&presta[j]) continue;
                    if(sta[i]&prepresta[p]) continue;
                    dp[k][i][j]=max(dp[k][i][j],dp[k-1][j][p]+num[i]);
                }
            }
        }
        for(i=1;i<=prenum;i++)   //把上一行给上上行
        {
            prepresta[i]=presta[i];
        }
        preprenum=prenum;
        for(i=1;i<=sum;i++)   //把当前行给上一行
        {
            presta[i]=sta[i];
        }
        prenum=sum;
    }
    ans=0;
    for(i=1;i<=sum;i++)
        for(j=1;j<=prenum;j++)
    {
        ans=max(ans,dp[N][i][j]);
    }
}
int main()
{
    int i,j;
    scanf("%d%d",&N,&M);
    getchar();
    for(i=1;i<=N;i++)
    {
        gets(ch[i]);
    }
    DP();
    printf("%d\n",ans);
    return 0;
}


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值