思路
因为题目的数据很小,所以直接二进制枚举就好了。
思路就是枚举每一行的二进制形式,1表示有人,0表示没人,dp[i][[j]表示第i行的放置状态是j
下面考虑下判断操作是否合法:
1、首先上一层 放了人的地方下一层必须不放,既上一层第 i位位1,下一层第i位必须为0,上一层第 i 位为0下一层可放可不放 , 这个操作可以用 & 运算符 j & k ,j表示上一层的状态,k表示这一层 ,当j & k 为 0 时说明这一层放置方法为k合理
2、本层Map[i] 的第j位为0表示这层不能放人,假设放置方法为 k ,则 (Map[i] & k) == k 表示操作合法(Map[i]的第j位为0如果k的第j位为0 用 & 后结果不等于 k
3、本层不能有连续的1存在,,所以用 j & (j >> 1)若不为0表示存在连续的 1
代码
#pragma GCC target("avx,sse2,sse3,sse4,popcnt")
#pragma GCC optimize("O2,O3,Ofast,inline,unroll-all-loops,-ffast-math")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define pb push_back
#define pll pair<ll,ll>
#define INF 0x3f3f3f3f
const int mod = 100000000;
const int maxn = 100005;
#define stop system("pause")
inline ll read(){
ll s = 0, w = 1; char ch = getchar();
while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
return s * w;
}
int Map[15];
int dp[15][1 << 15];
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
memset(dp,0,sizeof(dp));
memset(Map,0,sizeof(Map));
for(int i = 1 ; i <= n ; ++i){
for(int j = 0 ; j < m ;++j){
int x = read();
Map[i] = (Map[i] << 1) + x;
}
}
dp[0][0] = 1;
for(int i = 1 ; i <= n ; ++i){//第i行
for(int j = 0 ; j < (1 << m); ++j){//第i行的状态
if((j & (j >> 1)) || (Map[i] & j)!= j) continue;
for(int k = 0 ; k < (1 << m) ; ++k){//枚举上一行
if(j & k) continue;//j有0的k都行,j有1的k必须为0
dp[i][j] = (dp[i][j] + dp[i-1][k])%mod;
}
}
}
ll ans = 0;
for(int i = 0 ; i < (1 << m) ; ++i){
ans = (ans + dp[n][i]) % mod;
}
printf("%lld\n",ans);
}
}
京公网安备 11010502036488号