您的位置:首页 > 其它

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

2017-09-27 22:37 381 查看

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][h−1])

当(tx,ty)为未知陷阱时,设陷阱编号为tt,则

f[x][y][S][h]=max(f[x][y][S][h],f[tx][ty][S−3tt−1][h−1]∗g[S][tt]+

f[tx][ty][S−2∗3tt−1][h]∗(1−g[S][tt]))。

3、关于g数组

上面g[S][tt]表示当前状态为S时陷阱tt有害的概率。

预处理g,也就是枚举S,再枚举0到2K−1,选取有用的概率计入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

; double f

[C][V], gw[C][V];
bool vis

[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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: