洛谷 P1879 玉米田 状态压缩dp 优化版

农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。

遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。

John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)
输入输出格式
输入格式:

第一行:两个整数M和N,用空格隔开。

第2到第M+1行:每行包含N个用空格隔开的整数,描述了每块土地的状态。第i+1行描述了第i行的土地,所有整数均为0或1,是1的话,表示这块土地足够肥沃,0则表示这块土地不适合种草。

输出格式:

一个整数,即牧场分配总方案数除以100,000,000的余数。

输入输出样例
输入样例#1: 复制

2 3
1 1 1
0 1 0

输出样例#1: 复制

9

先说下我数组都是表示什么,dp【i】【j】表示第i行是j状态时,前i行的可行个数。F【i】表示题目给的每i层的状态。ok【】表示没有相邻的土地的状态。
主要有一点:搞懂F【i】&ok【j】 = ok【j】 这个东西,题目给的每行可能会有相邻的肥沃土地,而ok【】里存的都是没有相邻的土地。两个取&之后还等于ok【j】表示第i行可以取ok【j】这种状态,比如下边几个例子。

F	 110010100
ok   101010101 // F&ok = ok  不能,
可以看到ok里有的1在F中并没有,所以不会相等。

F    111111111 
ok   101010101 这种就是 F&ok == ok
 可以看到ok中的1都是原有的1。

然后就是看上下没有相邻的,也就是上一行1和这一行的1错开,也就是这两行&后==0;
当然前提是两个的状态都得可行,都得是ok【某一个】&F【i】 = ok【某一个】 并且 ok【某一个】 & F【i-1】 = ok【某一个】。
代码:

#include 
#define mod 100000000
#define ll long long
using namespace std;
int n, m, F[15], x;
int dp[15][1 << 15];
int ok[555];
int main()
{
    memset(dp, 0, sizeof(dp));
    memset(ok, 0, sizeof(ok));
    memset(F, 0, sizeof(F));
    cin >> m >> n;
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= n; j++)
        {
            cin >> x;
            F[i] = (F[i] << 1) + x;
        }
    x = 0;
    int full = (1 << n) - 1;
    for (int i = 0; i <= full; i++)
    if((i & (i<<1)) == 0) ok[++x] = i;  //这个就是优化的地方,这样下边只需要遍历x次,而不是full次。
    dp[0][0] = 1;
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= x; j++)
        {
            if((ok[j] & F[i]) == ok[j])   //如果这一行ok【j】这种状态可行
            {
                for(int k = 1; k <= x; k++)
                {	//找上一层那种状态可行并且和这一行可行状态错开。
                    if((ok[k] & F[i-1]) == ok[k] && !(ok[k] & ok[j]))
                    {
                        dp[i][ok[j]] = (dp[i][ok[j]] + dp[i-1][ok[k]]) % mod;
                        //都满足,就是这一层这种状态的个数加上上层可行状态的个数。
                    }
                }
            }
        }
    }
    ll sum = 0;
    for (int i = 1; i <= x; i++)
    sum+=dp[m][ok[i]];  //这个就是把最后一行每种可行状态相加就行,
    //如果不可行就是0,所以就不用再判断了。dp【i】【j】表示第i行是j状态时,前i行的可行个数
    
    sum%=mod;
    cout << sum << endl;
    return 0;

}

你可能感兴趣的:(状态压缩dp)