看到第一眼就發覺是壯壓DP
然後就三進位枚舉子集吧。
這題真是壯壓入門好題。。。
對於dp[i][j] 表示第i行,j狀態下前i行的分配方案數。
那麼dp[i][j]肯定是從i-1行轉過來的
那麼由於不能挨著放,那麼我們肯定是枚舉i - 1行狀態時不能包含j的任何一位。
那麼只要令k = ((1 << n) - 1) ^ j,k中肯定就不包含j的位了
是這樣枚舉k的子集
int sub = k;
do
{
sub = k& (sub - 1);
}while(sub != k);
然後對每個子集,判斷合法性,然後相加即可。
#include <iostream>#include <cstdio>#include <cstring>#include <vector>#include <algorithm>#define MAXN 1005#define INF 1000000000using namespace std;int dp[13][1 << 13];int n, m;int st[13];int mod = 100000000;bool ok(int s, int pos){ if((s | st[pos]) > st[pos]) return false; for(int i = 0; i < n; i++) if(s & (1 << i)) { if(s & (1 << (i + 1))) return false; } return true;}int main(){ int x; scanf("%d%d", &m, &n); for(int i = 1; i <= m; i++) { for(int j = 0; j < n; j++) { scanf("%d", &x); if(x) st[i] |= (1 << j); } } dp[0][0] = 1; for(int i = 1; i <= m; i++) { for(int k = 0; k < (1 << n); k++) { int s = ((1 << n) - 1) ^ k; if(ok(k, i)) { //printf("%d %d\n", k, s); dp[i][k] += dp[i - 1][0]; for(int j = s; j; j = s & (j - 1)) { if(ok(j, i - 1)) dp[i][k] = (dp[i][k] + dp[i - 1][j]) % mod; } } } } int ans = 0; for(int i = 0; i < (1 << n); i++) ans = (ans + dp[m][i]) % mod; printf("%d\n", ans); return 0;}