sgu138状压dp用dfs来统计

来源:互联网 发布:玉帝 天帝知乎 编辑:程序博客网 时间:2024/05/16 15:15

题目大意:有一个大小为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 0
原创粉丝点击