P2489 [SDOI2011]迷宫探险 概率dp
LINK:迷宫探险
题目中要求在最优的策略下的最大概率 而并非期望概率。
一个坑点 题目中虽然没有明说 但是 探险者是知道地图的模样和每个陷阱的概率的。
所以才有最优策略一说。
最优策略尽管不知道可以随便走取max即可。
容易想到 对于当前状态 有 x,y,hp,s 来描述 。倒着设状态 那就是当前状态能到达终点的最大概率。
定义hp s都是递增的 不过还是不能线性递推。存在问题 可能状态之间可以互相转移的问题。
显然状态转移回来是不必要的 所以此时概率为0 利用dfs栈可以很容易判断出来 所以考虑记忆化搜索。
不过这引出了另外一个问题 当前状态可能被多次访问到 不过当前状态被第一次访问到已经被标记了 此时可能不是最优的。
容易想到转移到当前状态最多只有4种可能都记录下来即可。
6.16 Update:被wn给hack掉了 发现上面的思路在能走回路的时候会挂掉。
原因:存在一种最优方案使得从某个点走到另外一个点然后再跑回来的情况。
同时也存在一开始就跑过去的情况是最优的。
实际上上述做法能解决第一种情况 而第二种情况就在第一种情况被解决时 被卡掉了。
因为 可以走完这步 发现无毒 然后一些较优的状态可能被一些情况给卡掉了 因为原地乱转的原因。
一个好的解决方法:强制不让其乱转 保证状态之间的拓扑关系 这样就不会出现问题了。
//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define get(x) x=read()
#define gt(x) scanf("%d",&x)
#define gi(x) scanf("%lf",&x)
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define gc(a) scanf("%s",a+1)
#define rep(p,n,i) for(RE int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define fep(n,p,i) for(RE int i=n;i>=p;--i)
#define vep(p,n,i) for(RE int i=p;i<n;++i)
#define pii pair<int,int>
#define mk make_pair
#define RE register
#define P 1000000007
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define EPS 1e-8
#define sq sqrt
#define mod 998244353
#define S second
#define F first
#define Set(a,v) memset(a,v,sizeof(a))
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
RE int x=0,f=1;RE char ch=getc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
return x*f;
}
//偷税吧 少年
const int MAXN=33,N=244;
int n,m,H,k,maxx,s1,s2,maxx1,ans;
int w[1<<5],b[N],id;
int vis[MAXN][MAXN][6][N];
db g[N][6],f[MAXN][MAXN][6][N];
char a[MAXN][MAXN];
int dx[4]={1,-1,0,0};
int dy[4]={0,0,-1,1};
vector<pii>v[MAXN][MAXN][MAXN];
int mark[MAXN][MAXN],cc[N];
inline int pd(int s,int x)
{
return s>>x-1&1;
}
inline void dfs(int x,int y,int s,int xx,int yy)
{
if(mark[x][y]==id)return;
mark[x][y]=id;
if(a[x][y]=='@'||(a[x][y]>='A'&&a[x][y]<='Z'&&!pd(s,a[x][y]-'A'+1)&&(x!=xx||y!=yy)))
{
v[s][xx][yy].pb(mk(x,y));return;
}
for(int i=0;i<4;++i)
{
int wx=dx[i]+x;
int wy=dy[i]+y;
if(wx<=0||wx>n||wy<=0||wy>m)continue;
if(a[wx][wy]=='#')continue;
dfs(wx,wy,s,xx,yy);
}
}
inline void prepare()
{
rep(1,n,i)rep(1,m,j)
{
if(a[i][j]=='#'||a[i][j]=='@')continue;
rep(0,maxx,s)
{
++id;
dfs(i,j,s,i,j);
}
}
}
inline db dp(int x,int y,int hp,int s)
{
if(vis[x][y][hp][s])return f[x][y][hp][s];
vis[x][y][hp][s]=1;
if(hp==0)return f[x][y][hp][s]=0;
if(a[x][y]=='@')return f[x][y][hp][s]=1;
db ans=0;int ww=cc[s];
vep(0,v[ww][x][y].size(),i)
{
int xx=v[ww][x][y][i].F;
int yy=v[ww][x][y][i].S;
if(a[xx][yy]=='@')ans=max(ans,dp(xx,yy,hp,s));
else
{
int ss=a[xx][yy]-'A'+1;
if(s/b[ss-1]%3==1)ans=max(ans,dp(xx,yy,hp-1,s));
if(s/b[ss-1]%3==0)
ans=max(ans,dp(xx,yy,hp-1,s+b[ss-1])*g[s][ss]
+dp(xx,yy,hp,s+b[ss-1]*2)*(1-g[s][ss]));
}
}
return f[x][y][hp][s]=ans;
}
int main()
{
//freopen("1.in","r",stdin);
gt(n);gt(m);gt(k);gt(H);
rep(1,n,i)
{
gc(a[i]);
rep(1,m,j)if(a[i][j]=='$')s1=i,s2=j;
}
maxx=1<<k;--maxx;b[0]=1;
rep(0,maxx,i)get(w[i]),ans+=w[i];
rep(1,k,i)b[i]=b[i-1]*3;
maxx1=b[k]-1;
rep(0,maxx1,i)
{
//0表示未知 1表示有毒 2表示无害.
int num=0;
rep(0,maxx,w1)//0表示无害 1表示有毒
{
int flag=0;
rep(1,k,w2)
{
if(i/b[w2-1]%3==0)continue;
if(i/b[w2-1]%3==1&&(w1>>w2-1&1))continue;
if(i/b[w2-1]%3==2&&!(w1>>w2-1&1))continue;
flag=1;
}
if(flag)continue;
num+=w[w1];
rep(1,k,j)
{
if(i/b[j-1]%3==1&&(w1>>j-1&1))continue;
if(i/b[j-1]%3==2&&!(w1>>j-1&1))continue;
if(i/b[j-1]%3==0)if(w1>>j-1&1)g[i][j]+=w[w1];
}
}
rep(1,k,j)
{
if(i/b[j-1]%3==1)g[i][j]=1;
if(i/b[j-1]%3==2)g[i][j]=0,cc[i]|=(1<<j-1);
if(i/b[j-1]%3==0)g[i][j]/=num;
}
}
prepare();
printf("%.3lf",dp(s1,s2,H,0));
return 0;
}