POJ3254 Corn Fields + POJ1185 炮兵阵地 状态压缩DP

都说这是两道状压DP的入门题emmmmm...搞了一下午好像明白了点???

1.POJ3254 Corn Fields 

先贴大佬博客Orz:https://blog.csdn.net/harrypoirot/article/details/23163485

讲的已经很好很详尽了Orz,我代码里的注释也基本仿照该博客的,膜拜三连Orz

直接上AC代码吧,以后还是要多翻回来看看。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;

const int mod=100000000;
const int N=13;
const int M=1<<13;
int n,m;//n行m列
int tol;//每行最多的状态数
int dp[N][M];//第i行,状态为j,可以放牛的种数
int sta[M];//存放每行所有的可行状态(即没有相邻的状态)
int cur[N];//第i行的情况

bool check(int x)//判断状态x是否可行
{//判断一个数相邻两位是不是同时为1
    if(x&x<<1)//同时为1返回一个值
        return false;
    else//否则返回 0
        return true;
}
bool judge(int x,int i)
{
    if(x&cur[i])//判断状态x与第i行的实际状态的"逆"是否"重合"
        return false;//若有重合,则x不符合要求
    else
        return true;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        memset(dp,0,sizeof(dp));
        memset(sta,0,sizeof(sta));
        memset(cur,0,sizeof(cur));
        int x;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&x);
                if(x==0)
                    cur[i]+=(1<<(m-j));//1表示不能放,0表示能放
            }
        tol=0;
        for(int i=0;i<(1<<m);i++)//注意状态从0开始
        {
            if(check(i))
                sta[++tol]=i;//可行状态
        }
        for(int k=1;k<=tol;k++)
        {
            if(judge(sta[k],1))
                dp[1][k]=1;
        }
        for(int i=2;i<=n;i++)
        {
            for(int k=1;k<=tol;k++)//找出一组与第i行相符的state[k]
            {
                if(!judge(sta[k],i))//判断是否符合第i行实际情况
                    continue;
                for(int kk=1;kk<=tol;kk++)//再找一组与第i-1行符合,且与第i行不冲突的状态state[kk]
                {
                    if(!judge(sta[kk],i-1))//判断是否符合第i-1行实际情况
                        continue;
                    if(sta[k]&sta[kk])//判断是否与第i行状态冲突
                        continue;
                    dp[i][k]=(dp[i][k]+dp[i-1][kk])%mod;
                }
            }
        }
        int ans=0;
        for(int i=1;i<=tol;i++)
            ans=(ans+dp[n][i])%mod;
        printf("%d\n",ans);
    }
	return 0;
}

2. POJ1185 炮兵阵地

同样先贴大佬博客:https://blog.csdn.net/qq_34374664/article/details/54900865

有了上面那道题的基础,这道题做起来顺手多了,没想到的就是dp开了三维的,还有就是num数组很巧妙。

也是直接贴上AC代码吧:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN=105;
const int MAXM=12;
const int MAX=1<<12;
int n,m;
int sta[70];//每一行可行的状态
int tol;
int cur[MAXN];//每一行的情况
int num[70];//记录每个可行状态里有多少个1
int dp[MAXN][70][70];//dp[i][j][k]表示在第i行状态为j,i-1行状态为k时的答案
// dp[i][j][k] = max(dp[i][j][k],dp[i-1][k][l] + num[i]);  //l:上上层状态

bool check(int x)
{
    if((x&(x<<2))||(x&(x<<1)))//注意两个条件
        return false;
    else
        return true;
}
bool judge(int x,int i)
{
    if(x&cur[i])
        return false;
    else
        return true;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        memset(cur,0,sizeof(cur));
        memset(sta,0,sizeof(sta));
        memset(dp,0,sizeof(dp));
        memset(num,0,sizeof(num));
        char s[MAXM];
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s+1);
            for(int j=1;j<=m;j++)
                if(s[j]=='H')
                    cur[i]+=(1<<(m-j));
        }
        tol=0;
        for(int i=0;i<(1<<m);i++)
            if(check(i))
            {
                sta[++tol]=i;
                int k=i,sum=0;
                while(k)
                {
                    if(k&1)
                        sum++;
                    k>>=1;
                }
                num[tol]=sum;//记录每个可行状态里有多少个1
            }
        for(int k=1;k<=tol;k++)
            if(judge(sta[k],1))
                dp[1][k][1]=num[k];
        for(int i=2;i<=n;i++)
        {
            for(int k3=1;k3<=tol;k3++)
            {
                if(!judge(sta[k3],i))
                    continue;
                for(int k2=1;k2<=tol;k2++)
                {
                    if(!judge(sta[k2],i-1))
                        continue;
                    if(sta[k3]&sta[k2])
                        continue;
                    for(int k1=1;k1<=tol;k1++)
                    {
                        if(!judge(sta[k1],i-2))
                            continue;
                        if((sta[k3]&sta[k1])||(sta[k2]&sta[k1]))
                            continue;
                        dp[i][k3][k2]=max(dp[i][k3][k2],dp[i-1][k2][k1]+num[k3]);
                    }
                }
            }
        }
        int ans=0;
        for(int k=1;k<=tol;k++)
            for(int kk=1;kk<=tol;kk++)
                ans=max(ans,dp[n][k][kk]);
        printf("%d\n",ans);
    }
	return 0;
}

动规真是门玄学QAQ...

猜你喜欢

转载自blog.csdn.net/Cc_Sonia/article/details/81430488
今日推荐