1. 程式人生 > >【BZOJ2246】[SDOI2011]迷宮探險(搜尋,動態規劃)

【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;
}