BZOJ 2246 [SDOI2011]迷宫探险 (记忆化搜索)
题目大意:太长了,略
并没有想到三进制状压
题解:
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 }