AcWing 327 玉米田


#include 

using namespace std;
const int MOD = 1e8;    //按1e8取模
const int N = 14;       //M*N个小方格,上限都是12,这里我们故意取大一点,到14.
const int M = 1 << 12;  //0~2^12-1,共2^12个状态
int n, m;               //n行,m列
int g[N];               //记录哪个位置是无法种田的,在题目中,输入的位置是0表示无法种田
vector st;         //哪些状态是本行合法的
vector head[M];    //某一个状态可以转化为哪些状态?
int f[N][M];            //状态压缩的DP数组,一维:完成了第i行,二维:在状态是j(二进制状态)的情况下

//状态检查是否合法,某个状态是不是存在连续1
bool check(int s) {
    for (int i = 0; i < m; i++)//遍历每一列
        //判断连续1的位运算办法,好科学啊~
        if ((s >> i & 1) && (s >> (i + 1) & 1)) return false;
    return true;
}

int main() {
    //优化输入
    ios::sync_with_stdio(false);
    cin >> n >> m;
    //输入地图
    for (int i = 1; i <= n; i++)        //n行
        for (int j = 1; j <= m; j++) {  //共m列
            int t;
            cin >> t;
            //如果这个位置不能选,也按照状态压缩的思路,转为整数数值存入到g数组中
            //后面在计算第i行某一位是不是无法耕种时,也可以快速用&运算获得结果
            if (t == 0)g[i] += 1 << (j - 1);//这是一个值得学习的技巧
        }

    //预处理,哪些状态是合法状态
    for (int i = 0; i < 1 << m; i++)
        if (check(i)) st.push_back(i);

    //预处理,所有合法状态,可以转化为哪些合法状态
    for (int i = 0; i < st.size(); i++)
        for (int j = 0; j < st.size(); j++) {
            int a = st[i], b = st[j];
            //此步骤只处理两行之间不冲突就算合理转化,不考虑无法耕种情况
            if ((a & b) == 0) head[i].push_back(j);
        }
    //开始DP
    f[0][0] = 1;//啥也不放算一种方案
    for (int i = 1; i <= n + 1; i++) //遍历每一行
        for (int j = 0; j < st.size(); j++)//遍历每一个状态
            for (int k = 0; k < head[j].size(); k++) {//j状态的每一个可行转化
                //如果此行的第j位无法耕种,那么不能转化
                if (g[i] & st[j])continue;//这个&运算用的太漂亮了
                //可以转化
                f[i][j] += f[i - 1][head[j][k]];
                //取模
                f[i][j] = f[i][j] % MOD;
            }
    //最后一行取结果
    printf("%d", f[n + 1][0] % MOD);
    return 0;
}