P1649 Obstacle Course S(dfs卡方向数组/bfs)

传送门icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1649

开始拿到题,发现数据范围是100,于是我尝试用dfs一做,写完后发现是90分,TLE一个点。于是乎观看题解(我还以为能剪枝掉),发现改一下遍历方向就能过了(我认为是数据不好)

大概是有其他的优化方法的(

dfs代码(90分/100分)

// Problem: 
//     P1649 [USACO07OCT] Obstacle Course S
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1649
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
using namespace std;
const int N=105;
int n;
char a[N][N];
int vis[N][N];
int sx,sy,ex,ey;
int minn=1e9;
int dx[4]={1,0,-1,0};
int dy[4]={0,-1,0,1};//这个方向很奇妙,其他方向就过不了,大概率应该是数据问题
//下  左 上 右
int pre[N][N];//某一步走的是什么,我最开始没想到不能往回走的因素,所以说,只需要判断这个和前面的一个相等不相等即可....这里的pre数组且一看吧
bool check;

void dfs(int xx,int yy,int k){//现在在某一个位置
	if(xx==ex&&yy==ey){
		minn=min(minn,k);check=true;return;//结束
		
	}
	if(k>=minn) return;//剪枝
	for(int i=0;i<4;++i){
		int x=xx+dx[i];int y=yy+dy[i];
		if(x>n||x<1||y<1||y>n||vis[x][y]) continue;
		vis[x][y]=1;
		if(xx==sx&&yy==sy) pre[x][y]=i,dfs(x,y,k);
		else{
			if(pre[xx][yy]==0||pre[xx][yy]==2){
				if(i==0||i==2) pre[x][y]=i,dfs(x,y,k);
			    else {
			    	 if(k<minn) pre[x][y]=i,dfs(x,y,k+1);
			    }
			}
			else if(pre[xx][yy]==1||pre[xx][yy]==3){
				if(i==1||i==3) pre[x][y]=i,dfs(x,y,k);//实在没想起来怎么快速判断,索性暴力
			    else {
			    	 if(k<minn) pre[x][y]=i,dfs(x,y,k+1);//不一样就转向
			    }
			}
		}
		vis[x][y]=0;
	}
}

int main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			cin>>a[i][j];
		    if(a[i][j]=='x') vis[i][j]=1;
		    if(a[i][j]=='A') sx=i,sy=j,vis[i][j]=1;
		    if(a[i][j]=='B') ex=i,ey=j; //结尾别标记,标记进不去了
		}
	}
	dfs(sx,sy,0);
	if(check) cout<<minn<<endl;
	else cout<<-1<<endl;
	
	return 0;
}

bfs思路:1.直接暴力维护(记录方向)    

2. 将一开始的扩展次数设为-1,然后,对每一次队头的元素进行无尽拓展(最开始是A),直到它走到边界或者障碍,可以证明(如果在一条线上,肯定会被拓展到,如果不在,就不会,这时候会被其他的队头扩展,又会增加)

该图片摘自洛谷 GNAQ的博客

如图所以,将A拓展到的加入队列,然后将某一个作为队头继续扩展(注意 被访问过就不处理 接着拓展)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值