【BZOJ2246】【codevs2135】迷宫探险,概率DP+记忆化搜索+状态压缩+运气

Time:2016.08.24
Author:xiaoyimi
转载注明出处谢谢


传送门1
传送门2
思路:
毒瘤题目
考虑 f[i][x][y][h] 表示走到(x,y),血量为h时已知陷阱状态为i的最大概率
转移的话向(x+1,y)(x-1,y)(x,y+1)(x,y-1)走(以下为用x’,y’表示)
I.如果(x’,y’)是已知的陷阱
1.有害-> f[i][x][y][h1]
2.无害-> f[i][x][y][h]
II是未知的陷阱,且陷阱种类为t
f[i][x][y][h1]g(i,t)+f[i′′][x][y][h](1g(i,t))
其中i’,i”分别表示知晓种类为t的陷阱后的新状态
g(i,t)表示陷阱状态为i时t种类陷阱的有害概率是多少
这样的话就比较清晰了
陷阱状态i可以用三进制数来表示——无害0,有害1,未知2
然后就是预处理g(i,t)
显然t在状态i中属于未知
这个可以暴力枚举状态i后处理其对每一种陷阱的影响
注意g(i,t)=P有害/(P有害+P无害)
思路还是比较好想的
但是真难调!
昨天晚上一直没调出来
原因
1.状态处理不当
把A当二(三)进制从小到大的第1位,B当第2位……
但是爆搜的时候给搞反了40->80
2.位运算自信不加括号,&优先级竟然比!=低,woc!80->90这里写图片描述
3.换一个枚举顺序就可以90->100???这里写图片描述

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define in(x,y,z) (x<=y&&y<=z)
using namespace std;
int n,m,k,h;
char s[33][33];
int p[33],a[6];
double g[250][6],f[250][33][33][6],tmp[2];
bool vis[250][33][33][6];
int dx[]={1,0,-1,0},dy[]={0,-1,0,1};
int change(int now,int pos,int val)
{
    for (int i=0;i<pos;++i)
        a[i]=now%3,now/=3;
    now+=val-2;
    for (int i=pos-1;i>=0;--i)
        now=now*3+a[i];
    return now;
}
void dfs(int wei)
{
    if (wei==k)
    {
        int now=0;
        for (int i=k-1;i>=0;--i) now=now*3+a[i];
        for (int i=0;i<k;++i)
            if (a[i]==2)
            {
                tmp[0]=tmp[1]=0;
                for (int j=0;j<(1<<k);++j)
                {
                    bool flag=0;
                    for (int l=0;l<k;++l)
                        if (a[l]==2) continue;
                        else if (((j>>l)&1)!=a[l])
                        {
                            flag=1;
                            break;
                        }
                    if (flag) continue;
                    tmp[((j>>i)&1)]+=p[j];
                }
                g[now][i]=tmp[1]/(tmp[1]+tmp[0]);
            }
        return;
    }
    a[wei]=0;
    dfs(wei+1);
    a[wei]=1;
    dfs(wei+1);
    a[wei]=2;
    dfs(wei+1);
}
double DP(int now,int x,int y,int h)
{
    if (!h) return 0;
    if (s[x][y]=='@') return 1;
    if (vis[now][x][y][h]) return f[now][x][y][h];
    vis[now][x][y][h]=1;
    for (int i=0;i<4;++i)
        if (in(1,dx[i]+x,n)&&in(1,dy[i]+y,m))
        {
            char ch=s[dx[i]+x][dy[i]+y];
            if (ch=='#') continue;
            else if (ch=='.'||ch=='@'||ch=='$') f[now][x][y][h]=max(f[now][x][y][h],DP(now,x+dx[i],y+dy[i],h));
            else if (ch>='A'&&ch<='Z')
            {
                int t=ch-'A',p=now;
                for (int j=0;j<t;++j) p/=3;
                if (p%3==0)
                    f[now][x][y][h]=max(f[now][x][y][h],DP(now,x+dx[i],y+dy[i],h));
                else if (p%3==1)
                    f[now][x][y][h]=max(f[now][x][y][h],DP(now,x+dx[i],y+dy[i],h-1));
                else
                    f[now][x][y][h]=max(f[now][x][y][h],DP(change(now,t,0),x+dx[i],y+dy[i],h)*(1-g[now][t])
                                                       +DP(change(now,t,1),x+dx[i],y+dy[i],h-1)*g[now][t]);
            }
        }
    return f[now][x][y][h];
}
main()
{

    scanf("%d%d%d%d",&n,&m,&k,&h);
    for (int i=1;i<=n;++i)
        scanf("%s",s[i]+1);
    for (int i=0;i<(1<<k);++i)
        scanf("%d",p+i);
    int x,y;
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j)
            if (s[i][j]=='$')
            {
                x=i;y=j;break;
            }
    int tot=1;
    for (int i=1;i<=k;++i) tot*=3;
    --tot;
    dfs(0);
    printf("%.3lf\n",DP(tot,x,y,h));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值