[Bzoj2246]迷宫探险(概率+DP)

Description

题目链接

Solution

用三进制表示陷阱状态,1表示有害,2表示无害,0表示不知道

\(f[S][i]\)表示状态为S时陷阱i有害的概率,这个可以预处理出

\(d[S][i][j][h]\)表示状态为S,在坐标\((i,j)\),血量为h时的答案

然后就可以DP了,记忆化搜索

Code

#include <cstdio>
#include <algorithm>
#define db double
#define Sta 300
#define N 36
using namespace std;

const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
db dp[Sta][N][N][6],f[Sta][N];
int n,m,k,h,p[N],sx,sy,A[6]; 
char g[N][N];

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

db tmp[2];
void dfs(int x){
    if(x==k){
        int S=0;
        for(int i=k-1;i>=0;--i) S=S*3+A[i];
        for(int i=0;i<k;++i){
            if(A[i]!=2) continue;
            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(A[l]!=((j>>l)&1)){flag=1;break;}
                if(flag) continue;
                tmp[(j>>i)&1]+=p[j];
            }
            f[S][i]=tmp[1]/(tmp[1]+tmp[0]);
        }
    }else for(int i=0;i<=2;++i){A[x]=i;dfs(x+1);}
}

int Change(int S,int pos,int x){
    for(int i=0;i<pos;++i) A[i]=S%3,S/=3;S-=x;
    for(int i=pos-1;i>=0;--i) S=S*3+A[i];
    return S;
} 

db DP(int S,int x,int y,int h){
    if(!h) return 0;
    if(g[x][y]=='@') return 1;
    db &tmp=dp[S][x][y][h];
    if(tmp!=-1) return tmp;
    tmp=0;
    
    for(int d=0;d<4;++d){
        int nx=x+dx[d],ny=y+dy[d],tS=S;
        if(nx<=0||ny<=0||nx>n||ny>m) continue;
        char ch=g[nx][ny];
        if(ch=='#') continue;
        else if(ch=='.'||ch=='$'||ch=='@') tmp=max(tmp,DP(S,nx,ny,h));
        else if(ch>='A'&&ch<='Z'){
            int id=ch-'A';
            for(int i=0;i<id;++i) tS/=3;
            if(tS%3==2) tmp=max(tmp,DP(Change(S,id,2),nx,ny,h)*(1-f[S][id])+DP(Change(S,id,1),nx,ny,h-1)*f[S][id]); 
            else tmp=max(tmp,DP(S,nx,ny,h-(tS%3)));
        }
    }
    return tmp;
}

int main(){
    n=read(),m=read(),k=read(),h=read();
    for(int i=1;i<=n;++i) scanf("%s",g[i]+1);
    for(int i=0;i<(1<<k);++i) scanf("%d",&p[i]);
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)if(g[i][j]=='$'){sx=i;sy=j;break;}
    int tot=1;for(int i=1;i<=k;++i)tot*=3;--tot;
    dfs(0);
    for(int i=0;i<Sta;++i)for(int j=1;j<N;++j)for(int k=1;k<N;++k)for(int l=0;l<6;++l)dp[i][j][k][l]=-1;
    printf("%.3lf\n",DP(tot,sx,sy,h));
    return 0;
}

转载于:https://www.cnblogs.com/void-f/p/8715813.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值