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

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

题面

BZOJ
洛谷

题解

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

似乎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;
}
posted @ 2018-11-04 22:55  小蒟蒻yyb  阅读(440)  评论(0编辑  收藏  举报