题目传送门

一、样例数据解析

原始数据
AcWing 327 玉米田

可能的摆放办法
AcWing 327 玉米田

二、棋盘类状态压缩DP套路总结

  1. 使用一维数组压缩存储不允许种田的位置
  for (int i = 1; i <= n; i++)        //n行
        for (int j = 1; j <= m; j++) {  //m列
            int t;
            cin >> t;
            //状态压缩,记录每一行的状态
            //1、这里只记录不能耕种,即t==0的情况,可以耕种的不用记录啦~
            //2、按二进制转十进制的思想进行记录每一位,从左到右,每次左移一位解决数位问题,理解后需要背诵下来
            if (!t)g[i] += 1 << (j - 1);
        }
  1. 预处理所有合法状态

  2. 预处理合法状态之间的兼容关系

  3. 三层循环,枚举每一行,枚举每一种状态,枚举每种状态的兼容前序状态,在合乎题意的前提下,尝试进行状态上的转化。

  4. 枚举最后一行的所有可能状态,累加出答案。

三、朴素版本代码

#include <bits/stdc++.h>

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<int> st;         //哪些状态是本行合法的
vector<int> head[M];    //某一个状态可以转化为哪些状态?
int f[N][M];            //一维:完成了第i行,二维:在状态是j(二进制状态)的情况下,值:方案数量

//状态检查是否合法,某个状态是不是存在连续1
bool check(int x) {
    return !(x & x >> 1);
}

int main() {
    //1、输入地图
    cin >> n >> m;
    for (int i = 1; i <= n; i++)        //n行
        for (int j = 1; j <= m; j++) {  //m列
            int t;
            cin >> t;
            //状态压缩,记录每一行的状态
            //1、这里只记录不能耕种,即t==0的情况,可以耕种的不用记录啦~
            //2、按二进制转十进制的思想进行记录每一位,从左到右,每次左移一位解决数位问题,理解后需要背诵下来
            if (!t)g[i] += 1 << (j - 1);
        }

    //2、预处理,哪些状态是合法状态
    for (int i = 0; i < 1 << m; i++) //0~ pow(2,m)-1,相当于遍历二进制的每一种可能性
        if (check(i)) st.push_back(i);//棋盘类,用check检查状态的合法性,记录合法状态

    //3、预处理,每个合法状态可以兼容哪些状态
    for (int a: st)
        for (int b: st)
            //此步骤只处理两行之间没有竖着的冲突就算合理转化,不考虑无法耕种情况
            if ((a & b) == 0) head[a].push_back(b);

    //4、开始DP
    f[0][0] = 1;//啥也不放算一种方案
    for (int i = 1; i <= n; i++)                    //枚举每一行
        for (int a: st) {       //枚举每一个合法状态
            if ((g[i] & a)) continue;
            //不能耕种的土地记录在g[i]里,某位为1表示此列不能耕种
            //st[j]的含义是某种合法状态
            //其它情况都是随意种
            for (int b: head[a])
                //如果两者 & 大于零,表示存在某位或某几位,不允许种,但偏要种,这是不行的
                f[i][a] = (f[i][a] % MOD + f[i - 1][b] % MOD) % MOD;
        }

    //结果
    int res = 0;
    for (int a: st) res = (res % MOD + f[n][a] % MOD) % MOD;
    printf("%d", res);
    return 0;
}

四、滚动数组优化版本代码

#include <bits/stdc++.h>

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<int> st;         //哪些状态是本行合法的
vector<int> head[M];    //某一个状态可以转化为哪些状态?
int f[2][M];            //一维:完成了第i行,二维:在状态是j(二进制状态)的情况下,值:方案数量

//状态检查是否合法,某个状态是不是存在连续1
bool check(int x) {
    return !(x & x >> 1);
}

int main() {
    //1、输入地图
    cin >> n >> m;
    for (int i = 1; i <= n; i++)        //n行
        for (int j = 1; j <= m; j++) {  //m列
            int t;
            cin >> t;
            //状态压缩,记录每一行的状态
            //1、这里只记录不能耕种,即t==0的情况,可以耕种的不用记录啦~
            //2、按二进制转十进制的思想进行记录每一位,从左到右,每次左移一位解决数位问题,理解后需要背诵下来
            if (!t)g[i] += 1 << (j - 1);
        }

    //2、预处理,哪些状态是合法状态
    for (int i = 0; i < 1 << m; i++) //0~ pow(2,m)-1,相当于遍历二进制的每一种可能性
        if (check(i)) st.push_back(i);//棋盘类,用check检查状态的合法性,记录合法状态

    //3、预处理,每个合法状态可以兼容哪些状态
    for (int a: st)
        for (int b: st)
            //此步骤只处理两行之间没有竖着的冲突就算合理转化,不考虑无法耕种情况
            if ((a & b) == 0) head[a].push_back(b);

    //4、开始DP
    f[0][0] = 1;//啥也不放算一种方案
    for (int i = 1; i <= n; i++)                    //枚举每一行
        for (int a: st) {       //枚举每一个合法状态
            f[i & 1][a] = 0;    //清零
            if ((g[i] & a)) continue;
            //不能耕种的土地记录在g[i]里,某位为1表示此列不能耕种
            //st[j]的含义是某种合法状态
            //其它情况都是随意种
            for (int b: head[a])
                //如果两者 & 大于零,表示存在某位或某几位,不允许种,但偏要种,这是不行的
                f[i & 1][a] = (f[i & 1][a] % MOD + f[i - 1 & 1][b] % MOD) % MOD;
        }

    //结果
    int res = 0;
    for (int a: st) res = (res % MOD + f[n & 1][a] % MOD) % MOD;
    printf("%d", res);
    return 0;
}

相关文章:

  • 2021-07-13
  • 2022-01-05
  • 2021-12-10
  • 2022-03-05
  • 2021-12-24
  • 2021-05-31
  • 2021-12-06
  • 2022-03-09
猜你喜欢
  • 2022-12-23
  • 2022-01-17
  • 2021-11-24
  • 2021-09-29
相关资源
相似解决方案