蒙德里安的梦想

题意

思考

题目分析
摆放方块的时候,先放横着的,再放竖着的。总方案数等于只放横着的小方块的合法方案数。

如何判断,当前方案数是否合法? 所有剩余位置能否填充满竖着的小方块。可以按列来看,每一列内部所有连续的空着的小方块需要是偶数个。

状态表示
f[i][j] 表示已经将前 i -1 列摆好,且从第 i−1列,伸出到第 i 列的状态是 j 的所有方案。其中j是一个二进制数,用来表示哪一行的小方块是横着放的,其位数和棋盘的行数一致。

状态转移
既然第 i 列固定了,我们需要看 第 i-2 列是怎么转移到到第 i-1列的(看最后转移过来的状态)。假设此时对应的状态是k(第i-2列到第i-1列伸出来的二进制数,比如00100),k也是一个二进制数,1表示哪几行小方块是横着伸出来的,0表示哪几行不是横着伸出来的。

它对应的方案数是 f[i−1,k],即前i-2列都已摆完,且从第i-2列伸到第i-1列的状态为 k 的所有方案数。

这个k需要满足什么条件呢?

首先==k不能和 j在同一行==(如下图):因为从i-1列到第i列是横着摆放的12的方块,那么i-2列到i-1列就不能是横着摆放的,否则就是1 3的方块了!这与题意矛盾。所以 k和j不能位于同一行。

既然不能同一行伸出来,那么对应的代码为(k & j ) ==0 ,表示两个数相与,如果有1位相同结果就不是0, (k & j ) ==0表示 k和j没有1位相同, 即没有1行有冲突。

既然从第i-1列到第i列横着摆的,和第i-2列到第i-1列横着摆的都确定了,那么第i-1列 空着的格子就确定了,这些空着的格子将来用作竖着放。如果 某一列有这些空着的位置,那么该列所有连续的空着的位置长度必须是偶数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long LL;

const int N = 12, M = 1 << N;

int n, m;
LL f[N][M];
vector<int> state[M];
bool st[M];

int main(){
while(cin>>n>>m,n||m){
for (int i = 0; i < 1 << n; i ++ )
{
int cnt = 0;
bool is_valid = true;
for (int j = 0; j < n; j ++ )
if (i >> j & 1)
{
if (cnt & 1)
{
is_valid = false;
break;
}
cnt = 0;
}
else cnt ++ ;
if (cnt & 1) is_valid = false;
st[i] = is_valid;
}
for (int i = 0; i < 1 << n; i ++ )
{
state[i].clear();
for (int j = 0; j < 1 << n; j ++ )
if ((i & j) == 0 && st[i | j])
state[i].push_back(j);
}

memset(f, 0, sizeof f);
f[0][0] = 1;
for (int i = 1; i <= m; i ++ )
for (int j = 0; j < 1 << n; j ++ )
for (auto k : state[j])
f[i][j] += f[i - 1][k];

cout << f[m][0] << endl;
}

return 0;

}

炮兵阵地

题意

求最多摆放多少炮兵

思考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int N=10,M=1<<N;
int f[2][M][M];
int mp[1010];
int cnt[M];
vector<int> fst;

bool check(int state,int x)
{
for (int i = 0; i < x; i ++ )
if ((state >> i & 1) && ((state >> i + 1 & 1) || (state >> i + 2& 1)))
return false;
return true;
}
int count(int state,int y)
{
int res = 0;
for (int i = 0; i < y; i ++ )
if (state >> i & 1)
res ++ ;
return res;
}

int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=0;j<m;j++){
char c;
cin>>c;
mp[i]+=(c=='H')<<j;
}
}
for (int i = 0; i < 1 << m; i ++ )
if (check(i,m))
{
fst.push_back(i);
cnt[i] = count(i,m);
}
for(int i=1;i<=n+2;i++){
for(int j:fst){
for(int k:fst){
for(int u:fst){
if((j&k)|(k&u)|(u&j)) continue;
if((mp[i-1]&j)|(mp[i]&k)) continue;
f[i&1][j][k] = max(f[i&1][j][k],f[(i-1)&1][u][j]+ cnt[k]);
}
}
}
}
cout<<f[(n+2)&1][0][0];
return 0;
}

题意

思考

1