传送门https://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拓展到的加入队列,然后将某一个作为队头继续扩展(注意 被访问过就不处理 接着拓展)