链接
题目描述
给出一个01矩阵,现在可以选择其中的若干个1点,但是不能相邻,问一共有多少种选法
样例输入
2 3
1 1 1
0 1 0
样例输出
9
思路
状压DP
设
f
i
,
j
f_{i,j}
fi,j为第i行,状态编号为j(后面会讲求法)的总方案数
那么很显然若当前行状态与上一行没有相同的位置都为1就可以转移
f
i
,
j
+
=
f
i
−
1
,
k
(
状
态
j
&
状
态
k
=
0
)
f_{i,j}+=f_{i-1,k}(状态_j \& 状态_k=0)
fi,j+=fi−1,k(状态j&状态k=0)
状态编号求法其实也不难
就是要求当前状态没有两个1相邻,并且不能在原状态为0的位置为1
特判一下就好了
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mo = 1e9;
int n, m, a[15][15], f[15][5050];
struct status
{
int size, arr[5050];
}g[15];
void get(int line, int now)
{
int cnt = 0;
for(int j = 0; j < (1 << n); ++j)
{
bool b = 0;
for(int k = n; k >= 1; --k)
if(!a[line][k] && (j & (1 << (n - k)))) {
b = 1;
break;
}//判断有无和原矩阵相冲突的情况
if(b) continue;
if((j & (j << 1)) || (j & (j >> 1)))//有无两个1相邻的情况
continue;
g[line].arr[++cnt] = j;
}
g[line].size = cnt;
}
int DP()
{
int ans = 0;
for(int i = 1; i <= g[1].size; ++i)
f[1][i] = 1;
for(int i = 2; i <= m; ++i)
for(int j = 1; j <= g[i].size; ++j)
{
f[i][j] = 0;
for(int k = 1; k <= g[i - 1].size; ++k) {
if(g[i].arr[j] & g[i - 1].arr[k]) continue;
f[i][j] += f[i - 1][k];
}
}
for(int i = 1; i <= g[m].size; ++i)
ans = (ans + f[m][i]) % mo;
return ans;
}
int main()
{
scanf("%d%d", &m, &n);
for(int i = 1; i <= m; ++i)
for(int j = 1; j <= n; ++j)
scanf("%d", &a[i][j]);
for(int i = 1; i <= m; ++i)
{
int now = 0;
for(int j = 1; j <= n; ++j)
now = (now << 1) + a[i][j];
get(i, now);
}
printf("%d", DP());
return 0;
}