【问题描述】
cza特别喜欢吃海苔,怎么吃也吃不够。cza的生日到来时,他的父母给他买了许许多多的海苔和一个生日蛋糕。海苔是一个1 × 2或2 × 1的长方形,而蛋糕则是一个n × m的矩阵。蛋糕上有一些蜡烛占据了位置,其他地方都可以放海苔。cza的父母让cza把海苔尽可能多的放在蛋糕上,但是海苔不能够重叠放置。cza想把海苔留着自己以后慢慢吃,可又不敢违背父母,于是他决定放一少部分在蛋糕上。为了不使父母起疑,cza必须确保放置完海苔后,蛋糕上不存在1 × 2和2 × 1的空白以放置更多的海苔。cza想知道这样得花多少海苔,请帮助他求出满足这样放置所需的最少海苔数。
【输入格式】
输入的第一行是蛋糕的规模n和m(注意是n行m列)
接下来的n行每一行含m个字符。每个字符要么是”.”,表示空白;要么是”*”,表示蜡烛
【输出格式】
输出文件只包含一个整数k,表示满足题目要求的最小海苔数。
【输入样例】
3 3
…
.*.
…
【输出样例】
3
【数据范围与约定】
对于30%的数据N<=5,M<=5。
对于100%的数据N<=70, M<=7。
【分析】状压DP + 搜索
- 看到数据范围 M≤7 ,很容易想到用一个7位二进制数表示某一行的每个位置上是否有海苔
- 因为在某一行上放海苔可能会影响到下一行的状态(在这一行放
2×1
的海苔),所以我们记
f[i][stai][stai+1]
,表示选取到第
i
行,第
i 行的状态为 stai ,第 i+1 行的状态为 stai+1 所放的最小海苔数 - 我们枚举上一次的状态来转移,对于这一次可能出现的状态及其所需放的海苔数,直接暴搜出来转移即可
- 那么
DP
初值为
f[0][2m−1][s1]=0
(
s1
表示第一行的蜡烛摆放状态,因为蜡烛所在位置是不能放的),这里我们假定存在第
0
行且其已经放满,因为在第
1 行某个位置不放其实是没有关系的 - 而最后的答案 Answer=Min(f[n][stan][0]) ,因为假定的第 n+1 行我们是不能放的
【代码】
#include <iostream>
#include <cstdio>
using namespace std;
const int Maxn = 0x3f3f3f3f;
const int N = 75, M = (1 << 7) + 5;
int f[2][M][M], a[N]; char c[10];
int i, j, k, n, m, Ans = Maxn;
inline void CkMin(int &x, const int &y) {if (x > y) x = y;}
inline void Dfs(const int &x, const int &opx, const int &opy, const int &opz, const int &stp)
//opx表示当前第i - 1行的海苔状态,opy表示当前第i行的海苔状态,opz表示当前第i + 1行的海苔状态
{
if (x > 0 && (opx & (1 << x - 1)) == 0 && (opy & (1 << x - 1)) == 0) return ;
if (x > 1 && (opy & (1 << x - 1)) == 0 && (opy & (1 << x - 2)) == 0) return ;
if (x == m) return CkMin(f[i & 1][opy][opz], f[i - 1 & 1][j][k] + stp);
if ((opy & (1 << x)) == 0 && (opz & (1 << x)) == 0)
Dfs(x + 1, opx, opy | (1 << x), opz | (1 << x), stp + 1);
if (x < m - 1 && (opy & (1 << x)) == 0 && (opy & (1 << x + 1)) == 0)
Dfs(x + 2, opx, opy | (1 << x) | (1 << x + 1), opz, stp + 1);
Dfs(x + 1, opx, opy, opz, stp);
}
int main()
{
freopen("cake.in", "r", stdin);
freopen("cake.out", "w", stdout);
scanf("%d%d", &n, &m);
for (i = 1; i <= n; ++i)
{
scanf("%s", c + 1);
for (int j = 1; j <= m; ++j)
if (c[j] == '*') a[i] |= (1 << j - 1);
}
int T = (1 << m);
for (j = 0; j < T; ++j)
for (k = 0; k < T; ++k) f[0][j][k] = Maxn;
f[0][T - 1][a[1]] = 0;
for (i = 1; i <= n; ++i)
{
for (j = 0; j < T; ++j)
for (k = 0; k < T; ++k) f[i & 1][j][k] = Maxn;
for (j = 0; j < T; ++j)
for (k = 0; k < T; ++k)
if (f[i - 1 & 1][j][k] < Maxn)
Dfs(0, j, k, a[i + 1], 0);
}
for (j = 0; j < T; ++j) CkMin(Ans, f[n & 1][j][0]);
printf("%d\n", Ans);
}