链接:https://ac.nowcoder.com/acm/problem/17872
来源:牛客网
来源:牛客网
题目描述
今天是阳光明媚,晴空万里的一天,CSL早早就高兴地起床走出寝室到校园里转悠。
但是,等到他回来的时候,发现他的校园卡不见了,于是他需要走遍校园寻找它的校园卡。CSL想要尽快地找回他掉的校园卡,于是便求助于OneDay帮他一起找。
OneDay和CSL在同一已知的地点出发,并以相同的速度(1格/秒)搜索校园,试求两人走遍校园的最短时间。
但是,等到他回来的时候,发现他的校园卡不见了,于是他需要走遍校园寻找它的校园卡。CSL想要尽快地找回他掉的校园卡,于是便求助于OneDay帮他一起找。
OneDay和CSL在同一已知的地点出发,并以相同的速度(1格/秒)搜索校园,试求两人走遍校园的最短时间。
输入描述:
第一行为两个整数n,m(1 ≤ n, m ≤ 4),表示地图的大小。接下来是n行m列的地图:X表示障碍物,S表示起点,O表示空地。障碍物不能直接经过,数据保证所有空地是可达的,起点有且只有一个。
输出描述:
输出一个整数表示两人共同走遍校园所需的最少时间。
思路(新手,勿喷qwq)
题目要求最少时间,再看看数据范围:4*4,一开始可能会考虑BFS,但是再仔细看看题目,题目要求的是遍历一遍校园中的所有空地,所以应该用DFS来做;
所以考虑用DFS遍历
DFS遍历:直接套模板即可
再看看题目条件,是两个人遍历
可能有的人会想,直接用O的个数/2,如果O的个数是奇数就再+1不就行了?
错误的,举例
SOOO
这种情况答案是3
所以可以考虑一下其他方法
我的想法是:开一个三维的vis数组[2][6][6],其中[0][][]记录一个人走过的路径,[1][][]记录另一个人走过的路径
那么怎么判断所有的O都被走过了呢?
很简单,只需要遍历一遍整个图,如果图中存在vis[0][][]与vis[1][][]都未为1(即都未走过的点),并且这个点不是X就行啦
至于如何DFS,我的想法是对所有可能的答案都做一遍DFS
所有可能的答案:指先计算出图中O的个数,记作cnt,那么可以知道,答案的范围一定在((cnt+1)/2)~cnt之间
至于怎么实现的,还是看代码吧,里面有注释,应该比较好懂了(DFS不要忘记回溯)
代码如下(含注释)
#include<bits/stdc++.h>
using namespace std;
char mp[6][6];
int vis[2][6][6];
int dir[4][2]= {{1,0},{0,-1},{0,1},{-1,0}}; //方向数组
int n,m,tmp,cnt=0,flag=0,xx,yy,lx;
void DFS(int lx,int x,int y,int num) { //lx:lx=0指第1个人遍历,lx=1指第2个人遍历,x,y:当前坐标,num:需要步数的剩余值
if(flag) return;
if(num==0) { //如果已经达到了要求的的值
if(lx==0) {
DFS(1,xx,yy,tmp); //如果当前是第1个人,那么切换到第2个人
return;
} else {
int flag2=0;
for(int i=0; i<n; i++) {
for(int j=0; j<m; j++) {
if(!vis[0][i][j] && !vis[1][i][j] && mp[i][j]!='X') { //判断图中的O点是否已经被两个人中的至少一个人遍历过了
flag2=1;
break;
}
}
if(flag2) break;
}
if(!flag2) { //如果前面的判断成立,说明O点已经被遍历完了,这个答案有效,同时因为题目要求最小值,所以答案就是这个tmp,直接输出即可
flag=1;
printf("%d\n",tmp);
}
return;
}
}
for(int i=0; i<4; i++) { //四个方向遍历
int xt=x+dir[i][0];
int yt=y+dir[i][1];
if(xt<0 || yt<0 || xt>=n || yt>=m || mp[xt][yt]=='X') continue;
vis[lx][xt][yt]++; //这里不能写成vis[][][]=1,因为主函数里面没用memset函数清空每一次查询的vis数组,所以为了保证S位置的标记一直在,需要用回溯和vis[][][]++和vis[][][]--
DFS(lx,xt,yt,num-1); //同一个人继续遍历
vis[lx][xt][yt]--; //回溯,这里不能写成vis[][][]=0,理由同上
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=0; i<n; i++) {
scanf("%s",mp[i]);
}
for(int i=0; i<n; i++) {
for(int j=0; j<m; j++) {
if(mp[i][j]=='O') { //记录O点的个数
cnt++;
}
if(mp[i][j]=='S') { //标记一下S的位置
xx=i;
yy=j;
vis[0][i][j]=1;
vis[1][i][j]=1;
}
}
}
int st=(cnt+1)/2,ed=cnt; //计算答案存在的范围
for(int i=st; i<=ed; i++) { //遍历所有可能的值,得出结果
tmp=i;
DFS(0,xx,yy,tmp);
if(flag) break;
}
return 0;
}
using namespace std;
char mp[6][6];
int vis[2][6][6];
int dir[4][2]= {{1,0},{0,-1},{0,1},{-1,0}}; //方向数组
int n,m,tmp,cnt=0,flag=0,xx,yy,lx;
void DFS(int lx,int x,int y,int num) { //lx:lx=0指第1个人遍历,lx=1指第2个人遍历,x,y:当前坐标,num:需要步数的剩余值
if(flag) return;
if(num==0) { //如果已经达到了要求的的值
if(lx==0) {
DFS(1,xx,yy,tmp); //如果当前是第1个人,那么切换到第2个人
return;
} else {
int flag2=0;
for(int i=0; i<n; i++) {
for(int j=0; j<m; j++) {
if(!vis[0][i][j] && !vis[1][i][j] && mp[i][j]!='X') { //判断图中的O点是否已经被两个人中的至少一个人遍历过了
flag2=1;
break;
}
}
if(flag2) break;
}
if(!flag2) { //如果前面的判断成立,说明O点已经被遍历完了,这个答案有效,同时因为题目要求最小值,所以答案就是这个tmp,直接输出即可
flag=1;
printf("%d\n",tmp);
}
return;
}
}
for(int i=0; i<4; i++) { //四个方向遍历
int xt=x+dir[i][0];
int yt=y+dir[i][1];
if(xt<0 || yt<0 || xt>=n || yt>=m || mp[xt][yt]=='X') continue;
vis[lx][xt][yt]++; //这里不能写成vis[][][]=1,因为主函数里面没用memset函数清空每一次查询的vis数组,所以为了保证S位置的标记一直在,需要用回溯和vis[][][]++和vis[][][]--
DFS(lx,xt,yt,num-1); //同一个人继续遍历
vis[lx][xt][yt]--; //回溯,这里不能写成vis[][][]=0,理由同上
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=0; i<n; i++) {
scanf("%s",mp[i]);
}
for(int i=0; i<n; i++) {
for(int j=0; j<m; j++) {
if(mp[i][j]=='O') { //记录O点的个数
cnt++;
}
if(mp[i][j]=='S') { //标记一下S的位置
xx=i;
yy=j;
vis[0][i][j]=1;
vis[1][i][j]=1;
}
}
}
int st=(cnt+1)/2,ed=cnt; //计算答案存在的范围
for(int i=st; i<=ed; i++) { //遍历所有可能的值,得出结果
tmp=i;
DFS(0,xx,yy,tmp);
if(flag) break;
}
return 0;
}