[BZOJ2246][SDOI2011]迷宫探险(状压&概率DP)

1、DP模型

3 进制数表示陷阱的状态,0表示无害, 1 表示有害,2表示未知。可建立DP模型:
f[x][y][S][h] 表示从 (x,y) 开始,当前陷阱的状态为 S ,血量为h,活着走出迷宫的概率。使用记忆化搜索。

2、边界&转移

边界为:
f[x][y][S][0]=0
(x,y) 为终点时 f[x][y][S][h]=1
转移为(以下 (tx,ty) (x,y) 走一步能到达的格子,且 (tx,ty) 不为墙):
(tx,ty) 为平地,起点,终点或无害陷阱时,
f[x][y][S][h]=max(f[x][y][S][h],f[tx][ty][S][h])
(tx,ty) 为有害陷阱时,
f[x][y][S][h]=max(f[x][y][S][h],f[tx][ty][S][h1])
(tx,ty) 为未知陷阱时,设陷阱编号为 tt ,则
f[x][y][S][h]=max(f[x][y][S][h],f[tx][ty][S3tt1][h1]g[S][tt]+
f[tx][ty][S23tt1][h](1g[S][tt]))

3、关于g数组

上面 g[S][tt] 表示当前状态为 S 时陷阱tt有害的概率。
预处理 g ,也就是枚举S,再枚举 0 2K1,选取有用的概率计入 g

4、总结

结合「SCOI2008奖励关」「JLOI2013卡牌游戏」两题可以得出,像这样有限制条件,且在转移的过程中限制条件不断变化的概率DP,一般模型为:
f[state]表示从 state 状态到达目标状态的最大/小概率/期望。

5、代码

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
inline char get() {
    char c; while (((c = getchar()) < 'A' || c > 'E') && c != '@'
        && c != '$' && c != '.' && c != '#'); return c;
}
const int N = 35, C = 267, R = 45, V = 10;
int m, n, K, H, dx[] = {1, 0, -1, 0}, dy[] = {0, -1, 0, 1}, pw[V], pb[R];
char s[N][N]; double f[N][N][C][V], gw[C][V];
bool vis[N][N][C][V];
double chkmax(double &a, double b) {a = max(a, b);}
int cyx(int S, int x) {
    return S / pw[x - 1] % 3;
}
int lpf(int S, int x, int y) {
    int res = S - cyx(S, x) * pw[x - 1];
    return res + y * pw[x - 1];
}
void init() {
    int i, j, S; for (S = 0; S < pw[K]; S++) {
        int s1 = 0;
        for (i = 0; i < (1 << K); i++) {
            bool flag = 1; for (j = 1; j <= K; j++) {
                int xx = cyx(S, j); if (xx == 2) continue;
                if (xx != ((i >> j - 1) & 1)) {flag = 0; break;}
            }
            if (!flag) continue; s1 += pb[i];
            for (j = 1; j <= K; j++) {
                if (cyx(S, j) != 2 || !((i >> j - 1) & 1)) continue;
                gw[S][j] += pb[i];
            }
        }
        for (i = 1; i <= K; i++) gw[S][i] /= s1;
    }
}
double DP(int x, int y, int S, int h) {
    if (vis[x][y][S][h]) return f[x][y][S][h];
    if (s[x][y] == '@') return vis[x][y][S][h] = 1, f[x][y][S][h] = 1;
    if (h == 0) return vis[x][y][S][h] = 1, f[x][y][S][h] = 0;
    vis[x][y][S][h] = 1; int i;
    for (i = 0; i < 4; i++) {
        int tx = x + dx[i], ty = y + dy[i], tt = s[tx][ty] - 'A' + 1;
        if (tx < 1 || tx > m || ty < 1 || ty > n || s[tx][ty] == '#')
            continue;
        if (s[tx][ty] == '.' || s[tx][ty] == '@' || s[tx][ty] == '$' ||
            (tt >= 1 && tt <= K && cyx(S, tt) == 0))
                chkmax(f[x][y][S][h], DP(tx, ty, S, h));
        if (tt >= 1 && tt <= K && cyx(S, tt) == 1)
            chkmax(f[x][y][S][h], DP(tx, ty, S, h - 1));
        if (tt >= 1 && tt <= K && cyx(S, tt) == 2)
            chkmax(f[x][y][S][h], DP(tx, ty, lpf(S, tt, 1), h - 1) * gw[S][tt]
                + DP(tx, ty, lpf(S, tt, 0), h) * (1.0 - gw[S][tt]));
    }
    return f[x][y][S][h];
}
int main() {
    int i, j, Sx, Sy;
    m = read(); n = read(); K = read(); H = read();
    for (i = 1; i <= m; i++) for (j = 1; j <= n; j++)
        if ((s[i][j] = get()) == '$') Sx = i, Sy = j; pw[0] = 1;
    for (i = 0; i < (1 << K); i++) pb[i] = read();
    for (i = 1; i <= K; i++) pw[i] = pw[i - 1] * 3; init();
    printf("%.3lf\n", DP(Sx, Sy, pw[K] - 1, H));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值