P1879 [USACO06NOV]玉米田Corn Fields

传送门

DP

注意虽然数据小但是搜索还是会超时

不能搜索

考虑DP:

但如果强行DP还是不够

那考虑优化:

每个格子只有两种状态:

选 or 不选 (当然如果这个格子是"贫瘠的"就一定不能选)

明显可以用一个2进制表示

而整行的状态就可以用一串2进制表示

状态的压缩就完成了

可能有人会疑惑n,m最大为12 , 2^12=4096 ,不会超时吗?

要注意虽然有4096种状态,但是真正有用到的状态只有不到500种

(显然 有用的状态就是 如果这一个状态2进制下这位为1,那么它上一位和下一位必定不能为1)

那就好了,用一个num数组存一下有用的状态就可以了

至于判断相邻两行的状态是否冲突也很简单:

if(num[ j ] & num[ k ]) 那么就冲突

转移方程:

设f[ i ][ j ]表示第 i 行的第 j 种状态有多少种方案:

则显然

  f[ i ][ j ]+=f[ i-1 ][ k ]     (状态j 与 状态 k 不冲突  且j和k都不与地图冲突)

那么最后的答案就是f[ n ][ i ]的和 (0<=i<=有用的状态总数)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstring>
using namespace std;
const int mo=100000000;
int n,m,a[15],b[15]; //b[i]是存第i行地图的状态(同样把状态压缩)
int f[15][500],num[500],cnt;//f和num如上,cnt存有用的状态总数
inline void out(int now)
{
    for(int i=m;i>=0;i--)
        if(now&(1<<i)) cout<<1;
        else cout<<0;
    cout<<endl;
}//输出一个数的二进制表示,有助于调试
int main()
{
    int res;
    cin>>n>>m;
    register int i,j;//用register int 会快一些
    for(i=1;i<=n;i++)
    {
        res=0;
        for(j=1;j<=m;j++)
            scanf("%d",&a[j]);//读入单行的状态
        for(j=0;j<m;j++)
            res+=((!a[m-j])<<j);//然后存在b中
        b[i]=res;
    }
    for(i=0;i<(1<<m);i++)
    {
        if(i&(i<<1)) continue;//判断此状态是否有用
        num[++cnt]=i;
    }
    f[0][1]=1;//初始化,第一种状态为什么也不放
    for(i=1;i<=n;i++)
        for(j=1;j<=cnt;j++)//枚举有用的状态
        {
            if(num[j]&b[i]) continue;
            for(int k=1;k<=cnt;k++)//同样枚举
            {
                if((num[j]&num[k])||(num[k]&b[i-1])) continue;//判断冲突
                f[i][j]=(f[i][j]+f[i-1][k])%mo;//更新
                //cout<<f[i][j]<<" "<<i<<" "<<j<<" "<<k<<endl;
                //out(num[k]);
            }
            //cout<<endl;
        }
    int ans=0;
    for(i=1;i<=cnt;i++)
        ans=(ans+f[n][i])%mo;
    cout<<ans;//输出
    return 0;
}
原文地址:https://www.cnblogs.com/LLTYYC/p/9508483.html