POJ 3254 Corn Fields [状压DP]

#Description
给你一个地图,叫你放东西。
0表示不能放,1表示可以放。
东西不能相邻放,问有多少种放法?

#= =
一开始黄队长和我说是状压DP。受前几天最优配对问题的影响,以为把所有情况状态压缩。一看数据2^(12*12)肯定是伤不起的。然后就GG。不知道怎么做

于是请教黄队长。
原来是每一行压缩。
#Algorithm
于是得到状态
dp[i][j] 表示第i行排列成j的总方案数
状态转移
dp[i][j] = dp[i - 1][k]
k表示所有和j不两个竖着连一起的状态
两个竖着不连一起显然是 j & k > 0
对于j,要考虑j必须满足当前行的状态,aka,不能再不能放的地方放。
j & ~g[i] > 0表示在不能放的地方上放了 continue;
j自己本身不能两个1连起来,j & (j << 1)这样就可以判断
最后把最后一行加起来就是ans

#Code

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n, m;
const int MAXN = 12;
const int MAXM = 12;
const int MOD = 1e8;
int g[MAXN];
int dp[MAXN][1<<MAXM];
void solve()
{
  memset(g, 0, sizeof(g));
  memset(dp, 0, sizeof(dp));
  for (int i = 0; i < n; i++) {
    g[i] = 0;
    for (int j = 0; j < m; j++) {
      int x;
      scanf("%d", &x);
      g[i] += (x << j);
    }
  }
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < (1 << m); j++) {
      if (j & (~g[i])) continue; //与此行不符
      if (j & (j << 1)) continue; //两个相连
      if (i == 0) {
        dp[i][j] = 1;
        continue;
      }
      for (int k = 0; k < (1 << m); k++) {
        if (j & k) continue; //竖着两个一样
        dp[i][j] = (dp[i][j] + dp[i - 1][k]) % MOD;
      }
    }
  }
  int ans = 0;
  for (int i = 0; i < (1 << m); i++) {
    ans = (ans + dp[n - 1][i]) % MOD;
  }
  cout << ans << endl;
}
int main()
{
  //freopen("in.txt", "r", stdin);
  while (scanf("%d%d", &n, &m) != EOF) {
    solve();
  }
}

猜你喜欢

转载自blog.csdn.net/YYecust/article/details/51924433