sgu138状压dp用dfs来统计

题目大意:有一个大小为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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值