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

题目大意:太长了,略

bzoj luogu

并没有想到三进制状压

题解:

3进制状压陷阱的状态,0表示这种陷阱的状态未知,1已知危险,2已知不危险

然后预处理出在当前状态下,每种陷阱有害的概率,设为$g[s][i]$

已知是危险的,有害概率为1

已知是不危险的,有害概率为0

未知的部分用概率表格里符合当前状态的部分,才是正确的(比如第4个样例输出了0.857就是没用这种方法去求概率)

定义$f[x][y][s][h]$表示当前在(x,y),陷阱的状态为s,当前血量是h

然后记忆化爆搜即可

...

 

 

 

此题解针对在luogu上交了,WA了第2个/第8个/第10个点,然后“换了个枚举顺序”就恰好A掉了这道题的情况

仔细观察发现上面那种做法貌似是有一些问题的

比如从上一层xxxx往下走↓,走到了yyyy这个状态,然后,yyyy还会往上跑从xxxx更新,得到了一个#$%@的“最优解”,这可能是yyyy往上跑的最优解,但也可能不是!

因为你状态xxxx可能还有某个方向没有遍历,但我们草率得把f[xxxx]这个“并不最优解”去更新f[yyyy]

那如果在另一次搜索中,由某个状态zzzz往上走↑,又跑到了yyyy,由于访问过了yyyy,所以返回了f[yyyy],然而这个f[yyyy]可能并不是最优解,导致答案出错!

为了避免这种错误,我们额外记录一维,表示从那个方向跑到当前状态,$f[x][y][s][h][t]$,t表示上一层是从哪个方向来的即可,虽然牺牲了一些常数但保证了答案的正确性!

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #define N 35
  5 #define M 250
  6 #define dd double
  7 #define idx(x) (x-'A'+1)
  8 using namespace std;
  9 
 10 int n,m,sx,sy,K,H;
 11 char str[N][N];
 12 int xx[]={-1,0,1,0},yy[]={0,1,0,-1};
 13 int pw[]={1,3,9,27,81,243,729};
 14 int mp[N][N],pro[M];
 15 dd f[N][N][M][7][5],g[M][7],dan[7];
 16 bool vis[N][N][M][7][5];
 17 bool check(int x,int y){
 18     if(x<1||x>n||y<1||y>m||mp[x][y]==-1) return 0;
 19     else return 1;}
 20 int p[M][7];
 21 dd dfs(int x,int y,int s,int h,int fa)
 22 {
 23     if(h<=0) return 0;
 24     if(vis[x][y][s][h][fa]) return f[x][y][s][h][fa];
 25     vis[x][y][s][h][fa]=1;
 26     if(mp[x][y]==K+1){
 27         f[x][y][s][h][fa]=1;
 28         return 1;
 29     }int tx,ty,t1,t2,pt;
 30     dd tmp=0;
 31     for(int i=0;i<4;i++)
 32     {
 33         tx=x+xx[i],ty=y+yy[i];
 34         if(!check(tx,ty)) continue;
 35         pt=mp[tx][ty],t1=t2=s;
 36         dd ans1=0,ans2=0;
 37         if(pt>0&&pt<=K&&!p[s][pt]) t1+=(1*pw[pt-1]);
 38         if(pt>0&&pt<=K&&!p[s][pt]) t2+=(2*pw[pt-1]);
 39         if(pt!=-1){
 40             if(g[s][pt]>0.0&&h>1) ans1=dfs(tx,ty,t1,h-1,(i+2)%4);
 41             if(g[s][pt]<1.0)ans2=dfs(tx,ty,t2,h,(i+2)%4);
 42             tmp=max(tmp,1.0*g[s][pt]*ans1+(1.0-g[s][pt])*ans2);
 43         }
 44     }f[x][y][s][h][fa]=tmp;
 45     return f[x][y][s][h][fa];
 46 }
 47 void Pre()
 48 {
 49     for(int i=0;i<(1<<K);i++)
 50         scanf("%d",&pro[i]);
 51     for(int i=0;i<pw[K];i++){
 52         int x=i,k=K;
 53         while(k){
 54             p[i][k]=x/pw[k-1];
 55             x%=pw[k-1],k--;}
 56     }
 57     for(int i=0;i<pw[K];i++)
 58     {
 59         int tot=0,sum;
 60         for(int j=0;j<K;j++)
 61             dan[j+1]=0;
 62         for(int s=0;s<(1<<K);s++){
 63             int fl=1;
 64             for(int j=0;j<K;j++)
 65                 if((s&(1<<j))&&p[i][j+1]==2) {fl=0;break;} 
 66                 else if((!(s&(1<<j)))&&p[i][j+1]==1) {fl=0;break;}
 67             if(!fl) continue;
 68             tot+=pro[s];
 69         }int x=i,k=K;
 70         for(int k=1;k<=K;k++)
 71         {
 72             if(p[i][k]==0){
 73                 sum=0;
 74                 for(int s=0;s<(1<<K);s++)
 75                 {
 76                     int fl=1;
 77                     for(int j=0;j<K;j++)
 78                         if((s&(1<<j))&&p[i][j+1]==2) {fl=0;break;} 
 79                         else if((!(s&(1<<j)))&&p[i][j+1]==1) {fl=0;break;}
 80                     if(!fl) continue;
 81                     if(s&(1<<(k-1))) sum+=pro[s];
 82                 }g[i][k]=1.0*sum/tot;
 83             }
 84             if(p[i][k]==1){g[i][k]=1.0;}
 85             if(p[i][k]==2){g[i][k]=0.0;}
 86         }
 87     }
 88 }
 89 int main()
 90 {
 91     scanf("%d%d%d%d",&n,&m,&K,&H);
 92     for(int i=1;i<=n;i++){
 93         scanf("%s",str[i]+1);
 94         for(int j=1;j<=m;j++)
 95         if(str[i][j]=='$') sx=i,sy=j;
 96         else if(str[i][j]=='@') mp[i][j]=K+1;
 97         else if(str[i][j]=='#') mp[i][j]=-1;
 98         else if(str[i][j]=='.') mp[i][j]=0;
 99         else mp[i][j]=idx(str[i][j]);
100     }
101     Pre();
102     printf("%.3lf\n",dfs(sx,sy,0,H,4));
103     return 0;
104 }

 

posted @ 2018-11-14 15:58  guapisolo  阅读(263)  评论(0编辑  收藏  举报