题目链接:
题目大意:
给一个
N×M
N
×
M
块土地,
1
1
表示这块土地肥沃, 表示土地贫瘠;现在只能在肥沃的土地上种玉米,问有多少种种法使得玉米两两不相邻 (相邻指的是上下走右四个方向) ?
Tips : 也可以一颗都不种。还要取模。
数据范围:
1≤N,M≤12 1 ≤ N , M ≤ 12
解题思路:
这个数据范围,状压DP啊!(好吧!看的题解)
dp[i][S]
d
p
[
i
]
[
S
]
表示第
i
i
行的 状态的方案数;
那么,
dp[i][S]
d
p
[
i
]
[
S
]
就由上一行所有的 与状态
S
S
不冲突 且 在上一行合法 的状态转移 过来,即
dp[i][S]+=dp[i−1][T]
d
p
[
i
]
[
S
]
+
=
d
p
[
i
−
1
]
[
T
]
;
其中 “与状态
S
S
不冲突” 是指种玉米的地方不相邻,“在上一行合法” 是指种玉米的地方得是肥沃的。
判断两个状态是否冲突,利用位运算能非常简单的解决:
bool is_conflict(int Sa, int Sb) { //判断两个状态是否冲突,冲突返回true
if(!(Sa & Sb) ) return false;
return true;
}
判断一个状态
S
S
在某一行是否合法,如果当前行状态取反之后和 相与的结果为
0
0
,那这个状态就是合法的!
代码中有解释;非常的精妙,学到了。
AC代码:
/********************************************
*Author* :XzzF
*Created Time* : 2018年04月15日 星期日 09时38分36秒
* Ended Time* : 2018年04月15日 星期日 10时42分53秒
*********************************************/
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const LL INF = 1LL << 60;
const int MOD = 100000000;
const int MaxN = 1 << 12;
int N, M;
int dp[14][MaxN + 5]; //dp[i][S]表示前i行的S状态的方案数
int field[14][14];
int valid_status[MaxN + 5]; //存合法的状态(即任意两个1不相邻)
int valid_cnt; //合法状态的个数
int fertile[14]; //fertile[i]表示第i行的土地状态
int all;
void Init() {
//找出所有合法的状态
memset(valid_status, 0, sizeof(valid_status));
valid_cnt = 0;
for(int i = 0; i < MaxN; i++)
if(!(i & (i << 1)))
valid_status[++valid_cnt] = i;
//处理每一行的土地状态
memset(fertile, 0, sizeof(fertile));
for(int i = 1; i <= N; i++)
for(int j = 1; j <= M; j++)
fertile[i] = ((fertile[i] << 1) | field[i][j]);
//初始化dp
all = (1 << M);
for(int i = 1; i <= valid_cnt; i++) {
int S = valid_status[i];
if(S < all && !(S & (~fertile[1])))
dp[1][S] = 1;
}
}
bool is_valid(int row, int S) { //判断S状态在第row行是否合法,合法返回true
if(S < all && !(S & (~fertile[row]))) return true;
return false;
}
bool is_conflict(int Sa, int Sb) { //判断两个状态是否冲突,冲突返回true
if( !(Sa & Sb) ) return false;
return true;
}
int main()
{
while(scanf("%d %d", &N, &M) != EOF)
{
for(int i = 1; i <= N; i++)
for(int j = 1; j <= M; j++)
scanf("%d", &field[i][j]);
Init();
for(int R = 2; R <= N; R++) {
for(int j = 1; j <= valid_cnt; j++) {
int pre_S = valid_status[j]; //枚举上一行的合法状态
if(is_valid(R - 1, pre_S)) {
for(int k = 1; k <= valid_cnt; k++) { //枚举当前行的状态
int cur_S = valid_status[k];
if(is_valid(R, cur_S) && !is_conflict(pre_S, cur_S)) {
dp[R][cur_S] = (dp[R][cur_S] + dp[R - 1][pre_S]) % MOD;
}
}
}
}
}
int ans = 0;
for(int i = 1; i <= valid_cnt; i++) {
if(valid_status[i] < all)
ans = (ans + dp[N][valid_status[i]]) % MOD;
}
printf("%d\n", ans);
}
return 0;
}