首页 > 其他分享 >P2489 [SDOI2011]迷宫探险 题解

P2489 [SDOI2011]迷宫探险 题解

时间:2023-02-24 13:57:29浏览次数:43  
标签:状态 P2489 int 题解 有害 迷宫 times SDOI2011 陷阱

题意简述:

一个 \(n\times m\) 的带墙体单入口多出口迷宫中有 \(k\) 个陷阱,陷阱分为有害或无害,有害会使人掉血,给出所有垃圾的有害与无害的所有排列组成的概率,给定人的血量,求掉最少血走出迷宫的概率。

解:

提到迷宫问题,考虑搜索。

首先将垃圾状态状压,\(0\) 为未知,\(1\) 为无害,\(2\) 为有害,由于一来所有陷阱的情况都是未知的,所以状态为 \(0\),而人需要去试探陷阱,每次试探后分有害无害搜索两种垃圾状态搜下去即可。

很明显会 \(T\),考虑优化,在迷宫中有限考虑记忆化。

设 \(f_{i,j,s,h}\) 代表人在 \((i,j)\),陷阱状态为 \(s\),生命值为 \(h\) 的概率。

那么我们需要提前预处理出来每一个三进制状态中一个未知的陷阱是有害的概率 \(g_{i,j}\)。

那么我们找到每一个合法的陷阱的组成方式,那么对于一个未知状态,它的概率即为所有组成方式的概率和除以组成方式个数。

那么记忆化也能转移了,对于踩到未知陷阱,\(f_{i,j,s,h}=f_{i,j,new1,h-1}\times g_{s,k}+f_{i,j,new2,h}\times (1-g{s,k})\),其中 \(new1,new2\) 代表踩到有害、无害陷阱的新状态,而 \(k\) 代表踩的是哪一种陷阱。

如果对于已知陷阱,踩到未知的不扣,踩到已知的扣血即可。

但是这样会有问题,因为有情况可以绕一圈然后再走出去,那么就导致了状态会未算完就转移了。

那么我们用拓扑序就能解决这个问题,对于每一种陷阱组成状态预处理出来自己下一步可以走到的未知、有害陷阱和出口,这样就处理出了这个问题。

预处理只需要枚举状态,枚举起始点坐标,然后对于每一个状态与初始坐标进行广搜记录能走到的点即可。

复杂度:\(O(n\times m\times 3^k\times h)\)。

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int N=35;
const int M=750;
const int K=6;
const double eps=1e-10;
int n,m,k,hp,xb,yb;
char s[N][N];
int p[M],power[6]={1,3,9,27,81,243},b[4][2]={{1,0},{0,1},{-1,0},{0,-1}},vis[N][N],cnt,r[M];
double g[M][K],f[N][N][M][K];
vector<pair<int,int>>v[N][N][1<<K];
void dfs(int x,int y,int q,int xx,int yy,int id)
{
	if(vis[x][y]==id)return;
	vis[x][y]=id;
	if((x!=xx||y!=yy)&&(s[x][y]=='@'||(s[x][y]>='A'&&s[x][y]<='Z'&&((1<<(s[x][y]-'A'))&q)==0)))
	{
		//cout<<xx<<" "<<yy<<" "<<q<<" "<<x<<" "<<y<<endl;
		v[xx][yy][q].push_back({x,y});
		return;
	}
	for(int i=0;i<4;i++)
	{
		int xt=x+b[i][0],yt=y+b[i][1];
		if(xt<1||yt<1||xt>n||yt>m||s[xt][yt]=='#'||(xx==xt&&yy==yt))continue;
		dfs(xt,yt,q,xx,yy,id);
	}
}
double dp(int x,int y,int q,int h)
{
	//cout<<x<<" "<<y<<" "<<q<<" "<<h<<endl;
	if(f[x][y][q][h]>=-eps)return f[x][y][q][h];
	if(h==0)return f[x][y][q][h]=0;
	if(s[x][y]=='@')return f[x][y][q][h]=1;
	int len=v[x][y][r[q]].size();
	double res=0;
	for(int i=0;i<len;i++)
	{
		int xx=v[x][y][r[q]][i].first,yy=v[x][y][r[q]][i].second;
		if(s[xx][yy]=='@')
		{
			res=max(res,dp(xx,yy,q,h));
			continue;
		}
		if(q/power[s[xx][yy]-'A']%3==2)res=max(res,dp(xx,yy,q,h-1));
		if(q/power[s[xx][yy]-'A']%3==0)
		{
			int xzt1=q+power[s[xx][yy]-'A']*2,xzt2=q+power[s[xx][yy]-'A'];
			res=max(res,dp(xx,yy,xzt1,h-1)*g[q][s[xx][yy]-'A']+dp(xx,yy,xzt2,h)*(1-g[q][s[xx][yy]-'A']));
		}
	}
	return f[x][y][q][h]=res;
}
int main()
{
	//freopen("maze.in","r",stdin);
	//freopen("maze.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&k,&hp);
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)for(int q=0;q<power[k];q++)for(int t=0;t<=hp;t++)f[i][j][q][t]=-1;
	for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(s[i][j]=='$')xb=i,yb=j;
	for(int i=0;i<(1<<k);i++)scanf("%d",&p[i]);
	for(int i=0;i<power[k];i++)
	{
		int num=0;
		for(int j=0;j<(1<<k);j++)
		{
			bool flag=0;
			for(int q=0;q<k;q++)
			{
				if(i/power[q]%3==0)continue;
				if(i/power[q]%3==1&&(j&(1<<q))==0)continue;
				if(i/power[q]%3==2&&(j&(1<<q)))continue;
				flag=1;
				break;
			}
			if(flag)continue;
			num+=p[j];
			for(int q=0;q<k;q++)
			{
				if(i/power[q]%3)continue;
				if((j&(1<<q)))g[i][q]+=p[j];
			}
		}
		for(int j=0;j<k;j++)
		{
			if(i/power[j]%3==1)g[i][j]=1,r[i]|=(1<<j);
			if(i/power[j]%3==2)g[i][j]=0;
			if(i/power[j]%3==0)g[i][j]/=num;
			//cout<<i<<" "<<j<<" "<<g[i][j]<<endl;
		}
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int q=0;q<1<<k;q++)if(s[i][j]!='#')dfs(i,j,q,i,j,++cnt);
	double ans=dp(xb,yb,0,hp);
	printf("%.3lf",ans);
	return 0;
}

标签:状态,P2489,int,题解,有害,迷宫,times,SDOI2011,陷阱
From: https://www.cnblogs.com/gmtfff/p/p2489.html

相关文章

  • P4416 [COCI2017-2018#1] Plahte 题解
    题意简述:给定\(n\)个互不相交,可以重叠的矩阵,对某些点染色,这个点上的所有矩阵会被染上这个颜色,求最后每个矩阵会有多少种颜色。解:我们可以先把矩阵拆成上下两条水平线......
  • CF1034A Enlarge GCD 题解
    题意简述:删去最少的数使所有的数的\(\text{gcd}\)增加。解:先对每个数除以所有数的\(\text{gcd}\),然后剩下的需要找到所有数的质因子,找到一个最多的序列中数拥有的质......
  • CF436E Cardboard Box 题解
    一道经典的反悔贪心题。考虑每次选择使总星数加一,那么总共有四种情况。一、对于一关没有星,选一颗星,代价为\(a_i\)。二、对于一关有一颗星,再选一颗星,代价为\(b_i-a_i\)......
  • CF204E Little Elephant and Strings 题解
    由于是多个串,还与每个子串的信息有关,很容易想到用SA或广义SAM。这里选择用SA。首先先把字符串转化为数组,连接起来,中间用一些不会出现的数。处理出后缀数组与\(height......
  • P5842 [SCOI2012]Blinker 的仰慕者 题解
    题意简述:求\([l,r]\)内各个数位积为\(k\)的数的和。解:在看题解前,请先确认自己是否精通了数位dp模板题,否则会很难理解。对于朴素的数位dp,我们可以记录\(f_{i,j......
  • P5416 [CTSC2016]时空旅行 题解
    首先题中的\(y,z\)好像没啥用……首先对于每一次询问,要求\(\min((x_0-x_i)^2+c_i)\)设\((x_0-x_i)^2+c_i=ans\)。那么\(x_0^2+x_i^2-2x_0x_i+c_i=ans\)所以\(x_0......
  • P7862 [COCI2015-2016#2] DRŽAVA 题解
    适当进行骗分是真的有用。\(40pts\):对于每两个点建立一条边,然后在贪心每次求最小边,在期间进行01背包即可,01背包用于处理模数。设\(dp_{i,j}\)代表以\(i\)为编号的一......
  • P1379 八数码难题 题解
    IDA*练习题由于题目问最小步数,很好想到可以用迭代式加深搜索,或是广搜,这里用的是深搜。枚举每次搜索的深度,也就是移动的步数,然后正常深搜,若达到目标解,返回\(\text{ture}......
  • P3213 [HNOI2011]勾股定理 题解
    据说是NP问题。很明显我们要先预处理出来勾股数对。但由于数过于大,所以常规的枚举是解决不了问题的。但也貌似没有什么很好的办法可以立马找到一个数的勾股数对。所以......
  • P3989 [SHOI2013]阶乘字符串 题解
    由于一些不可抗拒的原因,\(n\ge22\)无解。那么只用考虑\(n\le21\)的情况即可。由于\(n\)的范围缩小,导致状压又可以重新使用,所以考虑状压。设\(f_i\)为\(i\)中......