BZOJ.2246.[SDOI2011]迷宫探险(DP 记忆化搜索 概率)

题目链接

求最大的存活概率,DP+记忆化。
用f[s][x][y][hp]表示在s状态,(x,y)点,血量为hp时的存活概率。
s是个三进制数,记录每个陷阱无害/有害/未知。
转移时比较容易,主要是在陷阱未知时需要知道当前状态这个陷阱为有害/无害的概率,并用这两个概率相加。
如何求某个状态下未知陷阱是否有害的概率呢(以下求有害概率,即 有害/(有害+无害))
DFS枚举每个陷阱已知有害/无害/未知的状态,我们需要处理未知陷阱在该状态下的概率。
枚举每个未知的陷阱,再枚举2^K的概率数组,只有当满足所有已知陷阱的状态时(未知的有/无解都加),才可以更新当前陷阱有害/无害的概率。

这个概率数组感觉比较迷啊。。是K个陷阱满足该状态时的概率。
注意: 再回到一个点(如起点)是可行的!不要随便剪。。

//15480kb   156ms
#include <cstdio>
#include <cctype>
#include <algorithm>
const int N=33,to[5]={1,0,-1,0,1};

int n,m,K,K_2,H,pi[N],sta[6];
double P[255][6],tmp[2],f[N][N][6][255];
bool vis[N][N][6][255];
char mp[N][N];

void DFS(int x)
{
    if(x==K)
    {
        int now=0;
        for(int i=K-1; ~i; --i) now=now*3+sta[i];
        for(int p=0; p<K; ++p)
            if(sta[p]==2)
            {
                tmp[0]=tmp[1]=0;//该陷阱有害/无害的概率
                for(int i=0; i<K_2; ++i)
                {
                    bool f=1;
                    for(int j=0; j<K; ++j)
                        if(sta[j]==2) ;
                        else if(((i>>j)&1)!=sta[j]) {f=0; break;}
                    if(f) tmp[(i>>p)&1]+=pi[i];//!
                }
                P[now][p]=tmp[1]/(tmp[0]+tmp[1]);
            }
    }
    else
    {
        sta[x]=0, DFS(x+1);
        sta[x]=1, DFS(x+1);
        sta[x]=2, DFS(x+1);
    }
}
inline int Change(int s,int p,int to)
{
    int t=1; while(p--) t*=3;
    return s-(2-to)*t;
}
#define Now f[x][y][hp][s]
double Solve(int x,int y,int hp,int s)
{
    if(!hp) return 0;
    if(mp[x][y]=='@') return 1.0;
    if(vis[x][y][hp][s]) return f[x][y][hp][s];
    vis[x][y][hp][s]=1;//状态比较多不好判重啊。。直接在这设vis=1.
    for(int xn,yn,i=0; i<4; ++i)
    {
        xn=x+to[i], yn=y+to[i+1];
        if(!xn||!yn||xn>n||yn>m||mp[xn][yn]=='#') continue;
        char ch=mp[xn][yn];
        if(ch=='.'||ch=='@'||ch=='$') Now=std::max(Now,Solve(xn,yn,hp,s));
        else if(isalpha(ch)){
            int ts=s,id=ch-'A';
            for(int t=id; t; --t) ts/=3;
            if(!(ts%3)) Now=std::max(Now,Solve(xn,yn,hp,s));
            else if(ts%3==1) Now=std::max(Now,Solve(xn,yn,hp-1,s));
            else Now=std::max(Now,Solve(xn,yn,hp-1,Change(s,id,1))*P[s][id]+Solve(xn,yn,hp,Change(s,id,0))*(1-P[s][id]));
        }
    }
    return Now;
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&K,&H);
    int sx=0,sy;
    for(int i=1; i<=n; ++i)
    {
        scanf("%s",mp[i]+1);
        if(!sx){
            for(int j=1; j<=m; ++j)
                if(mp[i][j]=='$') sx=i,sy=j;
        }
    }
    K_2=1<<K;
    for(int i=0; i<K_2; ++i) scanf("%d",&pi[i]);
    DFS(0);
    int sta=1;
    for(int i=K; i; --i) sta*=3;
//  int sta=0;
//  for(int i=K; i; --i) sta=sta*3+2;
    printf("%.3lf",Solve(sx,sy,H,sta-1));

    return 0;
}

原文地址:https://www.cnblogs.com/SovietPower/p/8688493.html

时间: 2024-10-12 21:07:44

BZOJ.2246.[SDOI2011]迷宫探险(DP 记忆化搜索 概率)的相关文章

BZOJ2246 [SDOI2011]迷宫探险 【记忆化搜索dp + 概率】

题目 输入格式 输出格式 仅包含一个数字,表示在执行最优策略时,人物活着走出迷宫的概率.四舍五入保留3位小数. 输入样例 4 3 3 2 .$. A#B A#C @@@ 143 37 335 85 95 25 223 57 输出样例 0.858 提示 题解 毒瘤dp题 我们设\(f[x][y][s][h]\)表示从点\((x,y)\)出发,所有陷阱状态为\(s\),生命值为\(h\),存活的期望概率 我们枚举邻点,选择存活概率最大的作为当前\(f\)的值 除了墙,有以下情况: ①如果是空地或者终

11782 - Optimal Cut(树形DP+记忆化搜索)

题目链接:11782 - Optimal Cut 题意:按前序遍历给定一棵满二叉树,现在有k次,可以选k个节点,获得他们的权值,有两个条件: 1.一个节点被选了,他的子节点就不能选了. 2.最终选完后,根到所有叶子的路径上,都要有一个被选的节点. 思路:树形dp,dp[u][k]代表在结点u,可以选k个节点,那么就分两种情况 选u节点,dp[u][k] = node[u]; 选子节点之和,那么就把k次分配给左右孩子,dp[u][k] = max(dp[u][k], dp[u][i], dp[u]

[hihocoder 1033]交错和 数位dp/记忆化搜索

#1033 : 交错和 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0,?a1,?...,?an?-?1,定义交错和函数: f(x)?=?a0?-?a1?+?a2?-?...?+?(?-?1)n?-?1an?-?1 例如: f(3214567)?=?3?-?2?+?1?-?4?+?5?-?6?+?7?=?4 给定 输入 输入数据仅一行包含三个整数,l,?r,?k(0?≤?l?≤?r?≤?1018,?|k|

UVA - 10817 Headmaster&#39;s Headache (状压dp+记忆化搜索)

题意:有M个已聘教师,N个候选老师,S个科目,已知每个老师的雇佣费和可教科目,已聘老师必须雇佣,要求每个科目至少两个老师教的情况下,最少的雇佣费用. 分析: 1.为让雇佣费尽可能少,雇佣的老师应教他所能教的所有科目. 2.已聘老师必须选,候选老师可选可不选. 3.dfs(cur, subject1, subject2)---求出在当前已选cur个老师,有一个老师教的科目状态为 subject1,有两个及以上老师教的科目状态为 subject2的情况下,最少的雇佣费用. dp[cur][subje

UVa 10817 (状压DP + 记忆化搜索) Headmaster&#39;s Headache

题意: 一共有s(s ≤ 8)门课程,有m个在职教师,n个求职教师. 每个教师有各自的工资要求,还有他能教授的课程,可以是一门或者多门. 要求在职教师不能辞退,问如何录用应聘者,才能使得每门课只少有两个老师教而且使得总工资最少. 分析: 因为s很小,所以可以用状态压缩. dp(i, s1, s2)表示考虑了前i个人,有一个人教的课程的集合为s1,至少有两个人教的集合为s2. 在递归的过程中,还有个参数s0,表示还没有人教的科目的集合. 其中m0, m1, s0, s1, s2的计算用到位运算,还

poj1664 dp记忆化搜索

http://poj.org/problem?id=1664 Description 把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法. Input 第一行是测试数据的数目t(0 <= t <= 20).以下每行均包含二个整数M和N,以空格分开.1<=M,N<=10. Output 对输入的每组数据M和N,用一行输出相应的K. Sample Input 1 7 3 Sample Output 8 /

【DP】树形DP 记忆化搜索

DP中的树形DP,解决方法往往是记忆化搜索.显然,树上递推是很困难的.当然做得时候还是得把状态定义和转移方程写出来:dp[u][1/0]表示以u为根节点的树 涂(1) 或 不涂(0) 颜色的最少方案数.树上DP有两个经典问法:一条边两端至少有个一个端点涂色,问整个tree最少涂色次数:还有一种忘了...此题是前种问法. #include<cstdio> #include<cstring> #include<algorithm> using namespace std;

POJ 4968 DP||记忆化搜索

给出N个人的平局分X 根据GPA规则计算可能的最高平均GPA和最低平均GPA 可以DP预处理出来所有结果  或者记忆化搜索 DP: #include "stdio.h" #include "string.h" int inf=100000000; double a[11][1100],b[11][1100]; double Max(double a,double b) { if (a<b) return b; else return a; } double M

状压DP+记忆化搜索 UVA 1252 Twenty Questions

题目传送门 1 /* 2 题意:给出一系列的01字符串,问最少要问几个问题(列)能把它们区分出来 3 状态DP+记忆化搜索:dp[s1][s2]表示问题集合为s1.答案对错集合为s2时,还要问几次才能区分出来 4 若和答案(自己拟定)相差小于等于1时,证说明已经能区分了,回溯.否则还要加问题再询问 5 */ 6 /************************************************ 7 * Author :Running_Time 8 * Created Time :