都说这是两道状压DP的入门题emmmmm...搞了一下午好像明白了点???
1.POJ3254 Corn Fields
先贴大佬博客Orz:https://blog.youkuaiyun.com/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.youkuaiyun.com/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...