【状压dp】 poj 3254 Corn Fields

版权声明:转载标注来源喔~ https://blog.csdn.net/iroy33/article/details/89930256

题意:给m*n的场地,1代表可选,0代表不可选,选择的场地不能有边相邻(即不能上下左右四个方向),求选择的方案数,一个也不选也是一种选择方法。

思路:dp[i][j]代表考虑了前i行,第i行状态为j的方案数。枚举本行的可行状态,枚举上一行的状态

参考博客 

注意:位运算不加括号很容易出错!

注意与P1896 互不侵犯对比

区别1:P1896要求一定要放置完所有的国王,由于不知道在哪一行用完k个国王,所以最终结果是dp[i][j][k](遍历i,j,k是国王总数)之和,本题可以任选个数,因此没有第三维的限制,结果是考虑完所有行之后各个状态下的累加值。

区别2:P1896中每一行的可行状态(不考虑别的行的影响)都是相同的,因此可以先预处理出来,减少循环次数。本题中每一行的可行状态是不一样的,学到了 将每行值存为十进制,判断状态j在第i行是否可行  (a[i]&j)==j 则可行

加深了对这两题的理解,dp[i][j]还是考虑了前i行,在考虑第i行的时候将前i-1行的所有可转移状态都考虑过了,不可转移的也考虑过了(为0)

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=15;
int a[N];       //每一行表示的十进制数
int dp[N][1<<N]; //考虑前i行,第i行状态为j的方案数
const int mod=1e8;
int m,n;
int ans;
bool judge(int i,int j)     //判断j状态在第i行是否可行
{
    if((a[i]&j)!=j) return 0;   //如果a[i]二进制为1的位包含了J的,与值为j
    if((j&(j<<1))||(j&(j>>1))) return 0;        //水平方向相邻
    return 1;
}
void solve()
{
    int tot=(1<<n)-1;
    memset(dp,0,sizeof(dp));
    for(int i=0;i<=tot;++i)
    {
        if(judge(1,i)) dp[1][i]=1;      //这个就包含了状态为0 x&0==0 dp[1][0]=1
    }
    for(int i=2;i<=m;++i)
    {
        for(int j=0;j<=tot;++j)
        {
            if(judge(i,j))
            {
                for(int p=0;p<=tot;++p)
                {
                    if(j&p) continue;
                    dp[i][j]=(dp[i][j]+dp[i-1][p])%mod;
                }
            }
        }
    }
    for(int i=0;i<=tot;++i)
        ans=(ans+dp[m][i])%mod;
    cout<<ans<<endl;
}
int main()
{
    cin>>m>>n;
    for(int i=1;i<=m;++i)
    {
        a[i]=0;
        for(int j=1;j<=n;++j)
        {
            int tmp;
            cin>>tmp;
            a[i]=(a[i]<<1)+tmp;     //我去,不加括号的话好像会先算1+tmp。。。
        }
    }
    solve();
    return 0;

}

猜你喜欢

转载自blog.csdn.net/iroy33/article/details/89930256