【BZOJ2246】[SDOI2011]迷宫探险(搜索,动态规划)

【BZOJ2246】[SDOI2011]迷宫探险(搜索,动态规划)

题面

BZOJ
洛谷

题解

乍一看似乎是可以求出每个东西是陷阱的概率,然而会发现前面走过的陷阱是不是陷阱实际上是会对当前状态产生影响的。考虑一下状压,因为出了是陷阱和不是陷阱,还有一种情况是未知。所以三进制状压。
\(0\)表示是有害陷阱,\(1\)表示不是,\(2\)表示未知。
那么假如我们知道了一个当前的三进制状态,如何确定当前的某个未知的陷阱是否有害的概率呢?
这个显然可以暴力提前预处理出来。
那么这就很好办了,设\(f[x][y][hp][S]\)表示当前在\((x,y)\)位置,剩余血量为\(hp\),当前陷阱的状态集合为\(S\)的最大概率。那么直接记忆化爆搜就可以知道结果了。

似乎BZOJ上的数据有锅,怎么交都是T,我蒯别的题解也T了。。。。
然后我改成scanf就WA了,应该数据锅了,到洛谷交吧。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define cmax(a,b) (a=((a)<(b)?(b):(a)))
int bin[]={1,3,9,27,81,243,729,2187,6561};
int let(int x,int k){return x/=bin[k];}
int rit(int x,int k){return x*=bin[k];}
int get(int x,int k){x/=bin[k];return x%3;}
double f[35][35][729][10];
bool vis[35][35][729][10];
int n,m,K,H,P[50];
char g[35][35];
double p[729][10];
int bx,by,ex,ey;
int d[4][2]={1,0,0,-1,-1,0,0,1};
double dfs(int x,int y,int S,int hp)
{
    if(vis[x][y][S][hp])return f[x][y][S][hp];
    if(!hp)return f[x][y][S][hp]=0;
    if(g[x][y]=='@')return f[x][y][S][hp]=1;
    vis[x][y][S][hp]=true;
    for(int i=0;i<4;++i)
    {
        int xx=x+d[i][0],yy=y+d[i][1];
        if(xx<1||yy<1||xx>n||yy>m)continue;
        if(g[xx][yy]=='#')continue;
        if(g[xx][yy]>='A'&&g[xx][yy]<='Z')
        {
            int k=g[xx][yy]-65;
            if(get(S,k)<2)//Know
                cmax(f[x][y][S][hp],dfs(xx,yy,S,hp-get(S,k)));
            else//Unknow
                cmax(f[x][y][S][hp],dfs(xx,yy,S-bin[k],hp-1)*p[S][k]+dfs(xx,yy,S-bin[k]*2,hp)*(1-p[S][k]));
        }
        else
            cmax(f[x][y][S][hp],dfs(xx,yy,S,hp));
    }
    return f[x][y][S][hp];
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&K,&H);
    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]=='$')bx=i,by=j;
            else if(g[i][j]=='@')ex=i,ey=j;
    if(!ex){puts("0.000");return 0;}
    for(int S=0;S<bin[K];++S)
    {
        int sumP=0;
        for(int i=0;i<1<<K;++i)
        {
            bool fl=true;
            for(int j=0;j<K;++j)
            {
                int x=get(S,j);if(x==2)continue;
                if(((i>>j)&1)!=x){fl=false;break;}
            }
            if(!fl)continue;
            sumP+=P[i];
            for(int j=0;j<K;++j)
                if(get(S,j)==2&&(i&(1<<j)))p[S][j]+=P[i];
        }
        for(int j=0;j<K;++j)p[S][j]/=sumP;
    }
    printf("%.3lf\n",dfs(bx,by,bin[K]-1,H));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/cjyyb/p/9906442.html