【ACWing】327. 玉米田

题目地址:

https://www.acwing.com/problem/content/329/

农夫约翰的土地由 M × N M\times N M×N个小方格组成,现在他要在土地里种植玉米。非常遗憾,部分土地是不育的,无法种植。而且,相邻的土地不能同时种植玉米,也就是说种植玉米的所有方格之间都不会有公共边缘。现在给定土地的大小,请你求出共有多少种种植方法。土地上什么都不种也算一种方法。

输入格式:
1 1 1行包含两个整数 M M M N N N。第 2 , . . . , M + 1 2,...,M+1 2,...,M+1行:每行包含 N N N个整数 0 0 0 1 1 1,用来描述整个土地的状况, 1 1 1表示该块土地肥沃, 0 0 0表示该块土地不育。

输出格式:
输出总种植方法对 1 0 8 10^8 108取模后的值。

数据范围:
1 ≤ M , N ≤ 12 1≤M,N≤12 1M,N12

思路是动态规划,可以按行枚举,并且用状态压缩的方式存每行的玉米种植状态。比如,我们可以用一个整数的二进制位来枚举每个位置是否种玉米, 1 1 1表示种, 0 0 0表示不种。此外,如果同时用一个数组 A A A的第 i i i个数的二进制位来存第 i i i行的土地情况, 1 1 1表示不能种, 0 0 0表示能种,那么第 i i i行的有效状态就是那些与 A [ i ] A[i] A[i]做与运算等于 0 0 0的且不与第 i − 1 i-1 i1行矛盾的状态。设 f [ i ] [ s ] f[i][s] f[i][s]是第 i i i行的种植状态是 s s s的情况下,有多少种种植方案。那么可以按照第 i − 1 i-1 i1行的种植状态来分类,则有: f [ i ] [ s ] = ∑ ( t → s ) ∧ ( s & A [ i ] = 0 ) f [ i − 1 ] [ t ] f[i][s]=\sum_{(t\to s)\land (s\& A[i]=0)} f[i-1][t] f[i][s]=(ts)(s&A[i]=0)f[i1][t]初始条件 f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1。代码如下:

#include <iostream>
#include <vector>
using namespace std;

const int N = 15, M = 1 << 12, mod = 1e8;
int n, m;
int a[N], f[N][M];
// state存每行的合法状态(即没有两个连续的1的状态),
// head存state[i]能转移到的合法状态(即没有矛盾的状态)
vector<int> state, head[M];

bool check(int st) {
    
    
    for (int i = 0; i < n - 1; i++)
        if ((st >> i & 1) && (st >> i + 1 & 1))
            return false;
    return true;
}

int main() {
    
    
    cin >> m >> n;
    for (int i = 1; i <= m; i++)
        for (int j = 0; j < n; j++) {
    
    
            int s;
            cin >> s;
            a[i] += !s << j;
        }

    for (int i = 0; i < 1 << n; i++)
        if (check(i))
            state.push_back(i);

    for (int i = 0; i < state.size(); i++)
        for (int j = 0; j < state.size(); j++) {
    
    
            int a = state[i], b = state[j];
            if (!(a & b)) head[i].push_back(j);
        }

    f[0][0] = 1;
    for (int i = 1; i <= m; i++)
        for (int cur = 0; cur < state.size(); cur++)
            for (int prev : head[cur])
                if (!(state[cur] & a[i]))
                    f[i][cur] = (f[i][cur] + f[i - 1][prev]) % mod;
    
    int res = 0;
    for (int i = 0; i < state.size(); i++) res = (res + f[m][i]) % mod;

    cout << res << endl;

    return 0;
}

时间复杂度 O ( m 2 2 n ) O(m2^{2n}) O(m22n),实际并没有这么高,因为行合法的状态是远小于 2 n 2^n 2n的,空间 O ( m 2 n ) O(m2^n) O(m2n)

猜你喜欢

转载自blog.csdn.net/qq_46105170/article/details/114513928