题目大意:有一个大小为N * M的蛋糕,蛋糕上面有k根蜡烛,现在要求你在蛋糕上面铺1 * 2和2 * 1的巧克力
使得所铺的巧克力最少,且蛋糕上面没有空余地方可放巧克力了(只存在1 * 1的没铺的方格)
解题思路:1 * 1的空闲方格由三行决定,上一行,当前行和下一行,如果只考虑两行的话,就比较难了
所以我们用dp[i][s1][s2]表示第i行的状态是s1,第i + 1行的状态是s2的情况下放的最少巧克力数量
如此的话,可得到递推方程
dp[i][s1][s2] = min(dp[i][s1][s2], dp[i-1][s3][s4] + cnt)
解释上面方程的意思:在第i-1行的状态是s3,第i行的状态是s4的情况下,在第i行和第i+1上铺巧克力,使第i行的状态变成s1,第i+1行的状态变成s2,统计出铺在第i行和第i+1行的巧克力数量cnt,这样的话,状态转移就完成了
现在的问题是如何更新,更新第i行的话有可能有影响到第i+1行,所以传入两个状态,第i行和第i+1行的状态,然后dfs暴力枚举出放的巧克力,统计一下即可
这里在更新第N行的时候,需要用到第N+1行,又因为第N+1行是外界的行,所以最后的答案是
min(dp[N][s1][0]),第N+1行不能被占用,如果被占用,就表示放的巧克力超出边界了
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
int n, m;
char str[75][10];
int dp[2][130][130];
int s[75];
int tmpps, tmpns, pre, now;
void dfs(int ps, int ns, int nts, int ni, int cnt) {
if (ni > 0 && ((ps>>(ni - 1))&1) == 0 && ((ns>>(ni - 1))&1) == 0) return;//检测i-1行与i行是不是满足题意,横着放或者竖着放
if (ni > 1 && ((ns>>(ni - 1))&1) == 0 && ((ns>>(ni - 2))&1) == 0) return;//如果不满足就直接退出就好了
if (ni == m) {
dp[pre^1][ns][nts] = min(dp[pre^1][ns][nts], dp[pre][tmpps][tmpns] + cnt);
return;
}
dfs(ps, ns, nts, ni + 1, cnt);//如果第i-1行与第i行配合就已经把第i行的ni位置搞定了就接着往下搜,而不必使用第i+1行了。
//这两个是如果上面所说的情况不符合,那么就要用到i+1行了,横着放和竖着放两种情况,然后在往下搜索。
if (ni < m - 1 && ((ns>>ni)&1) == 0 && ((ns>>(ni + 1))&1) == 0) dfs(ps, ns|(1<<ni)|(1<<(ni + 1)), nts, ni + 2, cnt + 1);
if (((ns>>ni)&1) == 0 && ((nts>>ni)&1) == 0) dfs(ps, ns|(1<<ni), nts|(1<<ni), ni + 1, cnt + 1);
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%s", str[i]);
for (int j = 0; j < m; j++)
if (str[i][j] == '*') s[i] |= (1<<j);
}
int ss = (1<<m);
pre = 0, now = 1;
for (int i = 0; i < ss; i++)
for (int j = 0; j < ss; j++)
dp[now][i][j] = INF;
dp[now][ss - 1][s[1]] = 0;
for (int k = 1; k <= n; k++) {
swap(now, pre);
for (int i = 0; i < ss; i++)
for (int j = 0; j < ss; j++)
dp[now][i][j] = INF;
for (int i = 0; i < ss; i++) {
if (i&s[k - 1] != s[k - 1]) continue;//i中可以包含s,也就是说s是0的位置,i中可以是1
for (int j = 0; j < ss; j++) {
if (j&s[k] != s[k]) continue;//这个也是同理
if (dp[pre][i][j] == INF) continue;//第i-1行还能放,就直接跳过
tmpps = i; tmpns = j;//记录现在枚举的这两个状态
dfs(i, j, s[k + 1], 0, 0);
}
}
}
int ans = INF;
for (int i = 0; i < ss; i++)
ans = min(ans, dp[now][i][0]);
printf("%d\n", ans);
return 0;
}