[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; }
相关文章推荐
- 【BZOJ2246】[SDOI2011]迷宫探险【搜索】【概率DP】
- BZOJ.2246.[SDOI2011]迷宫探险(DP 记忆化搜索 概率)
- BZOJ2246 [SDOI2011]迷宫探险 【记忆化搜索dp + 概率】
- BZOJ2707 [SDOI2012]走迷宫 【概率dp + tarjan + 高斯消元】
- BZOJ 2246 [SDOI2011]迷宫探险 ——动态规划
- 【BZOJ2246】【codevs2135】迷宫探险,概率DP+记忆化搜索+状态压缩+运气
- [BZOJ2286][SDOI2011]消耗战(虚树+树形DP)
- 概率dp-九度-1546-迷宫问题
- zujt ZJUT 1423 地下迷宫 高斯消元法 概率DP
- bzoj 2286: [Sdoi2011消耗战 虚树+树形dp
- hdu 4050 2011北京赛区网络赛K 概率dp ***
- 【bzoj2286】[Sdoi2011消耗战 虚树+dp
- 【BZOJ 2553】[BeiJing2011]禁忌 AC自动机+期望概率dp
- BZOJ 2286: [Sdoi2011]消耗战 虚树 树形dp
- 【SDOI2011】黑白棋(Nim游戏&&DP)
- 【bzoj2286】【sdoi2011】【消耗战】【虚树+dp】
- bzoj 2286 [Sdoi2011]消耗战(虚树+树上DP)
- hdu 4035 2011成都赛区网络赛E 概率dp ****
- NKOJ 2654 (SDOI 2011)消耗战 (虚树+树形DP)
- bzoj 2337: [HNOI2011]XOR和路径 (概率与期望DP+高斯消元)