BZOJ2246 SDOI2011 迷宫探险(状压+概率dp)
题面太长了,贴起来好麻烦,走链接吧:
P2489 [SDOI2011]迷宫探险
题目指向状压。自然地考虑用二进制表示状态,0为无害,1为有害。紧接着会发现,当我们走到某个点 ( x, y ) 时,我们并不一定能确定地图上其它点是否无害。(比如说,在我们走到陷阱A的第一个位置之前,我们不能确定A是否有害,而我们完全有可能在走到 ( x, y)之前没有去过A)。
所以,应该用三进制表示状态:0为无害,1为有害,2为未知。
方便起见,我们预处理 Power[ i ] 表示 3 ^ i 。
假定我们已经处理出一个数组 g,g[ S ][ j ] 表示在S状态下第 j 类陷阱有害的概率。
显然我们可设四维状态: f[ x ][ y ][ S ][ hp ] 表示走到 ( x, y ) 、状态为 S 、生命值为 hp 时存活的概率。
最终我们要输出的是 f[ sx ][ sy ][ Power[ k ]-1 ][ h ],sx、sy为起点,Power[ k ]-1 即为全部未知的状态。因为我们起初着手解决这个问题时面对的就是这样的状态。
以下默认S是三进制表示。
那么考虑状态转移方程:
设 ( tx, ty ) 为 ( x,y ) 的下一步,t 为 ( tx, ty ) 的陷阱类型(处理为数字表示)。
当 hp=0 时,f[ x ][ y ][ S ][ hp ] =0;
当 ( x, y ) 为终点时, f[ x ][ y ][ S ][ hp ] =1;
当 ( x, y ) 确认为无害陷阱或为起点、终点、平地时,f[ x ][ y ][ S ][ hp ] = max{ f[ tx ][ ty ][ S ][ hp ] };
当 ( x, y ) 确认有害时, f[ x ][ y ][ S ][ hp ] = max { f[ tx ][ ty ][ S ][ hp-1 ] };(要掉血)
当 ( x, y ) 未知时, f[ x ][ y ][ S ][ hp ] =max { g[ S ][ t ] * f[ tx ][ ty ][ S’ ][ hp-1 ] + ( 1 - g[ S ][ t ] ) * f[ tx ][ ty ][ S” ][ hp ]};
其中 S’ 表示在原来S的基础上把第 t 位状态改为 1, S” 表示第 t 位状态改为 0。如何实现呢?我们先看 g 数组。
由于状态是三进制,所以很多操作都比二进制麻烦得多(因为不能直接位运算)。但是在理解的时候当成二进制or十进制数会比较容易想通。
读入的 2 ^ k 个数存在数组 p 中。
外循环枚举S,内循环枚举 i(此处 i 表示 p的下标),接下来我们要做三件事。
第一,判断当前 S 与 i 是否冲突。比如 i 的第 j 位为1,表示当前状态下陷阱 j 有害,那么如果S 的第 j 位为 0,则会造成冲突。
int Take(int S,int j)
{return (S/Power[j-1])%3;
}for(int j=1;j<=k;++j)
{int x=Take(S,j);//取出S中的第j位。具体为什么用十进制的感觉意会一下,很容易想通。if(x==2) continue;if(x!=((i>>(j-1))&1)){//不一样则冲突。flag=0;break;}
}
第二,将 i 状态下每一个在的 g[ S ][ j ] 累加 p[ i ]。
for(int j=1;j<=k;++j)
{
int x=Take(S,j);if(x==2&&((i>>(j-1))&1)) g[S][j]+=p[i];
}
没什么好说的。
最后,因为 g[ S ][ j ] 表示的是概率。这里引入一个公式: P( A | B ) = P ( A·B ) / P( B )。
即,在B已发生的条件下,A发生的概率为AB同时发生的概率除以B发生的概率。没有系统地学过这一块,只能借例说明了。比如当前有三个互斥事件X、Y、Z,A为X、Y均发生的概率,B为Y、Z均发生的概率。那么显然可得了。
具体在这道题中,A可为状态S下 j 有害,B可为状态S下的所有合法情况。那么我们开一个变量Pb,在做完第一件事后 Pb+=p[ i ] ,则据公式可得 g[ S ][ j ] /= Pb。
绕回来,上面求 S’ 和 S” 的实现办法。
int Remake(int S,int j,bool flag)
{return(S-Take(S,j)*Power[j-1]+flag*Power[j-1]);
}
luogu题解里学到的小技巧。感觉很妙。flag传的是你想要修改成的第 j 位的状态。很优美地完成了。
还有一点要解释。状态转移方程里提及 “ 当 ( x, y ) 确认为无害陷阱或为起点、终点、平地时,f[ x ][ y ][ S ][ hp ] = max{ f[ tx ][ ty ][ S ][ hp ] } ” ,为什么是起点也要执行呢?起初我以为没有影响,但是实践证明确实会影响最终答案。在杨神的睿智引导(?下,是因为对于某个点 ( x, y ),假定有两条路径可以走向终点,路径 p1 不经过起点且较小,另一条路径 p2 经过起点且概率较大(就算 p2 包含了从起点走向另一条路的一部分,但是对于 ( x, y) 它也是要考虑的)。因为我们是取 max,如果不处理经过起点的情况,那么无疑会导致 ( x, y ) 的值偏小。实际上WA的结果也确实是这样。
至此就没有其它大问题要阐明的了。虽然用了很多时间也调了很久,但是终于做出来并且敲完这篇博客,还是非常开心的。
细节的处理见code。
#include<bits/stdc++.h>
using namespace std;int m,n,k,h;
char Map[35][35];
int sx,sy;
int p[35];int Power[6];double g[245][6];
double f[35][35][245][6];
bool vis[35][35][245][6];int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};int Take(int S,int j)
{return (S/Power[j-1])%3;
}int Remake(int S,int j,bool flag)
{return(S-Take(S,j)*Power[j-1]+flag*Power[j-1]);
}void Init()
{scanf("%d%d%d%d",&m,&n,&k,&h);memset(vis,0,sizeof(vis));for(int i=1;i<=m;++i)for(int j=1;j<=n;++j){char s=getchar();for(;(s<'A'||s>'E')&&s!='.'&&s!='@'&&s!='$'&&s!='#';s=getchar());Map[i][j]=s;if(s=='$') sx=i,sy=j;}for(int i=0;i<(1<<k);++i) scanf("%d",&p[i]);
}void Prepare()
{Power[0]=1;for(int i=1;i<=k;++i) Power[i]=(Power[i-1]<<1)+Power[i-1];memset(g,0,sizeof(g));for(int S=0;S<Power[k];++S)//枚举状态[ 0,Power[k] )。{int Pb=0;for(int i=0;i<1<<k;++i){int flag=1;for(int j=1;j<=k;++j){int x=Take(S,j);//取出S中的第 j 位。 if(x==2) continue;if(x!=((i>>(j-1))&1)){// 是否冲突。 flag=0;break;}}if(!flag) continue;Pb+=p[i];for(int j=1;j<=k;++j){int x=Take(S,j);if(x==2&&((i>>(j-1))&1)) g[S][j]+=p[i];}//将i状态下每一个在的g[S][j]累加 p[i]。} for(int j=1;j<=k;++j) g[S][j]/=(1.000*Pb);}
}double dp(int x,int y,int S,int hp)
{ if(vis[x][y][S][hp]) return f[x][y][S][hp];vis[x][y][S][hp]=1;if(hp==0) return f[x][y][S][hp]=0.000;if(Map[x][y]=='@') return f[x][y][S][hp]=1.000;for(int i=0;i<4;++i){int tx=x+dx[i];int ty=y+dy[i];int t=Map[tx][ty]-'A'+1;if(Map[tx][ty]=='#'||tx<1||tx>m||ty<1||ty>n) continue;if(Map[tx][ty]=='@'||Map[tx][ty]=='.'||Map[tx][ty]=='$'||(t>0&&t<=k&&Take(S,t)==0)) f[x][y][S][hp]=max(f[x][y][S][hp],dp(tx,ty,S,hp));if(t>0&&t<=k&&Take(S,t)==1) f[x][y][S][hp]=max(f[x][y][S][hp],dp(tx,ty,S,hp-1));if(t>0&&t<=k&&Take(S,t)==2) f[x][y][S][hp]=max(f[x][y][S][hp],g[S][t]*dp(tx,ty,Remake(S,t,1),hp-1)+(1-g[S][t])*dp(tx,ty,Remake(S,t,0),hp));}return f[x][y][S][hp];
}void Work()
{memset(f,0,sizeof(f));printf("%.3lf",dp(sx,sy,Power[k]-1,h));
}int main()
{Init();Prepare();Work();return 0;
}
感谢中国。
BZOJ2246 SDOI2011 迷宫探险(状压+概率dp)相关推荐
- bzoj2246: [SDOI2011]迷宫探险
2246: [SDOI2011]迷宫探险 Time Limit: 10 Sec Memory Limit: 512 MB Submit: 202 Solved: 118 [Submit][Stat ...
- [BZOJ2246][SDOI2011]迷宫探险(状压概率DP)
1.DP模型 用33进制数表示陷阱的状态,00表示无害,11表示有害,22表示未知.可建立DP模型: f[x][y][S][h]f[x][y][S][h]表示从(x,y)(x,y)开始,当前陷阱的状态 ...
- HDU 4336 Card Collector(状压 + 概率DP 期望)题解
题意:每包干脆面可能开出卡或者什么都没有,一共n种卡,每种卡每包爆率pi,问收齐n种卡的期望 思路:期望求解公式为:$E(x) = \sum_{i=1}^{k}pi * xi + (1 - \sum_ ...
- BZOJ2246 [SDOI2011]迷宫探险 【记忆化搜索dp + 概率】
题目 输入格式 输出格式 仅包含一个数字,表示在执行最优策略时,人物活着走出迷宫的概率.四舍五入保留3位小数. 输入样例 4 3 3 2 .$. A#B A#C @@@ 143 37 335 85 9 ...
- P2473 奖励关 状压概率DP求期望
题目链接 https://www.luogu.com.cn/problem/P2473 题意 n轮,m种球,每轮随即一个球,可以接可以不接,接的话需要满足这个球的前置条件(比如3号球接球必须已经接过1 ...
- 【BZOJ2246】[SDOI2011]迷宫探险(搜索,动态规划)
[BZOJ2246][SDOI2011]迷宫探险(搜索,动态规划) 题面 BZOJ 洛谷 题解 乍一看似乎是可以求出每个东西是陷阱的概率,然而会发现前面走过的陷阱是不是陷阱实际上是会对当前状态产生影响 ...
- 2021 ccpc 哈尔滨 G. Damaged Bicycle 状压 + 期望dp
文章目录 题意: 思路: 传送门 题意: 你需要从111走到nnn,初始速度是ttt,某些地方有自行车,每个位置自行车有pip_ipi的概率是坏掉的,如果自行车没坏可以骑上自行车,速度是rrr,可以 ...
- bzoj4565 [HAOI2016]字符合并 结论+状压+区间dp
如果k==2的话就是记搜, 但这个题用记搜的思路的话是需要枚举k个断点的, 所以对于枚举断点,就很可能有优化,比如到一个断点,一个决策的最优值 于是考虑区间dp模型,相当于是插入一个数,然后看影响. ...
- BZOJ 2246 [SDOI2011]迷宫探险 (记忆化搜索)
题目大意:太长了,略 bzoj luogu 并没有想到三进制状压 题解: 3进制状压陷阱的状态,0表示这种陷阱的状态未知,1已知危险,2已知不危险 然后预处理出在当前状态下,每种陷阱有害的概率,设为$ ...
最新文章
- 哪些书你看之前以为很枯燥,结果一看却欲罢不能的?
- 我的Objective-C系列文章
- 学生们喜欢在计算机教室上课用英语怎么说,说说学习本课的感想,并谈谈在信息技术课堂上如何体现以学生为主体,教师为主导?...
- 不同串口通信速率超时时间_串口知识详解 串口功能及电路介绍
- Tomcat URL重写
- 如何编辑PDF文件,教你几招轻松搞定
- 电脑c盘怎么清理_电脑C盘内存不足?三分钟教你彻底清理C盘空间,瞬间多出10个G...
- java类的命名规范_一篇搞定Java命名规范
- Oh-My-Zsh 下远程ssh的乱码问题
- YYF的一些简单配置 2016-10-13
- SCI期刊名英文缩写查询
- C语言求解圆周率近似值
- 使用NetworkInterface类获得网络接口信息
- 使用Python快速实现人脸融合
- 用HTML语言编写下图所示网页,2019-02-21第一章 HTML基础
- windows无法更改密码解决办法
- 在进化计算中,软件进行元基编码的新陈代谢方式 V0. 1. 0
- 区分统一社会信用代码、组织机构代码、注册号
- Tracer 记录 Controller 日志
- java图片处理---Javax.imageIO包的用法
热门文章
- 10个设计最经典的web2.0网站
- EXCEL数据插入hivev表格
- Pyramid With Super Resolution for In-The-Wild Facial Expression Recognition
- MQ实现DEMO-入门
- 基于搜狗微信搜索获取公众号文章的阅读量及点赞量
- (01)ORB-SLAM2源码无死角解析-(01) 环境搭建,demo运行,ROS一键安装_清除各种疑难杂症
- 学编程没人带?推荐10个免费学编程的最佳网站给你
- 如何确保多用户商城系统的有效性
- STM32F103C8T6的高低电平范围
- 家用云服务器配置,如何选择合适的云服务器配置?