Day 5 查找搜索(思路部分搬运自乐学帖子)

成语接龙

Description
小张非常喜欢与朋友们玩成语接龙的游戏,但是作为“文化沙漠”的小张,成语的储备量有些不足。现在他的大脑中存储了 m 个成语,成语中的四个汉字都用一个1000000以内的正整数来表示。现在小张的同学为了考验他给出了他一个成语做开头和一个成语做结尾,如果小张能通过成语接龙的方式说到结尾的成语,他就能够成功完成游戏。他想知道最少能说几个成语能够成功完成游戏。
Input
第一行一个数 m(1 \leq m \leq 300000 ) 。

接下来 m 行,每行4个1000000以内的正整数,表示一个成语。

下一行4个1000000以内的正整数,表示开始成语。

下一行4个1000000以内的正整数,表示结束成语。

保证开始成语和结束成语在小张的成语储备之中。

Output
一行一个整数,表示最少说几个成语能够完成游戏。如果无法完成输出-1。
Notes
三个成语分别是(1,2,3,4)(4,5,6,7)(7,8,9,10)
思路:
【21-成语接龙】
(更得有点晚,如果大家做完了也可以对照一下思路)

【解题思路】
本题题意较为清晰,要找一种成语接龙的方式,使得开始成语和结束成语可以通过成语接龙的方式接起来。换言之,要找一条路径,可以从开始成语接龙到结束成语。

一开始我的思路是:建立一张图,把成语视作节点,两个成语可以接龙,那么就在它们之间连接一条有向边。但后来我发现,由于最多有M=3e5个成语,万一这两个成语两两都可以接到一起,那么边的数量将是M^2级别的,空间复杂度直接炸掉。

思路差不多,但是空间炸了,有没有什么其他方法呢?注意到题目中给了一个数据,那就是每个成语都是用4个1e6以内的正整数表示的,这个数据如果用不到就不会凭空给出。

于是,我们选择将每个成语的首字和尾字视作节点,把成语当做有向边(例如成语1 2 3 4,我们就从1到4建立一条有向边)这样节点最多有1e6个,边数最多有3e5条,空间复杂度完美,同时我们也实现了接龙的操作,因为一个成语中间的两个字是什么不重要,而可以接龙的成语由于首字和尾字的对应关系,被自动归入了一个节点,自然完成了接龙。

图建立出来了,那么答案是什么呢?是开始成语的首字到结束成语的尾字跑最短路吗?并不是。这是因为如果从开始成语的首字出发,跑出的最短路可能并非经过开始成语的这条边;同理,也可能并非经过结束成语的边。因此,我们要从开始成语的尾字往结束成语的首字跑最短路,跑出的距离+2即是答案(这里加的是开始和结束的成语)

当然,如果开始成语和结束成语一样,那么直接输出1即可,这里需要特判。

【代码细节】
建立一张点数为N=1e6,边数为m的图,将每个成语的首字和尾字连起来,用SPFA/dijkstra跑单源最短路径即可,(建议用SPFA,毕竟这节标题是搜索[doge])
【坑点】
注意答案的转换,究竟是从谁到谁跑最短路,以及和真正答案相差多少的问题

注意开始和结束成语相同输出1的情况
代码:`#include
#include
#include
using namespace std;

struct junction{
int end;
int judge;
junction(int end_,int judge_):end(end_),judge(judge_){}
};

struct time{
int begin;
int end;
};
vector g[400000];
queue q;
int dfs(int a,int b){
q.push(b);
while(!q.empty()){
int temp=q.front();
for(int i=0;i<g[temp].size();i++){
if(g[temp][i].judge0){
if(g[temp][i].end
a){
return 1;
}
g[temp][i].judge=1;
q.push(g[temp][i].end);
}
}
q.pop();
}
return 0;
}

int main(){
int n,m;
scanf("%d %d\n",&n,&m);
for(int i=0;i<m;i++){
struct time temp;
scanf("%d %d\n",&temp.begin,&temp.end);
g[temp.begin].push_back(junction(temp.end,0));
int flag=dfs(temp.begin,temp.end);
if(flag){
printf("%d\n",i+1);
return 0;
}
}
printf("-1\n");
return 0;
}`

地下城与勇士

Description
龙神觉得无聊于是来到了地下城,这里是一个巨大的迷宫,有一些可以通行的路、一些不可以通行的墙,还有一些怪物。虽然龙神可以轻易地干掉这些怪物,但他觉得这样就太没意思了,他观察到这些怪物每 k 秒会消失一次(例如 k 等於 3 时,则第 3 逗號 空格 6 逗號 空格 9 逗號 空格 中线省略号 秒怪物是消失的),每一秒龙神可以选择向上下左右行走一步(不能在原地不动)。龙神想知道在避开全部怪物的条件下,到达出口所需要的最短时间。

Input
第一行输入一个整数 T 空格 左括号 1 小於等於 T 小於等於 10 右括号,代表用例组数。

每组用例的第一行包括三个整数 n 逗號 空格 m 空格 左括号 1 小於等於 n 逗號 空格 m 小於等於 100 右括号 和 ,k 空格 左括号 1 小於等於 k 小於等於 50 右括号 分别表示地下城迷宫的行数、列数、怪物的消失间隔。

接下来的 n 行代表迷宫,.表示可以通行的路,#表示墙,*表示怪物,S表示起点,E代表出口。

Output
输出一个整数,表示龙神走出地下城迷宫的最短时间,如果龙神不能走出迷宫则输出-1。

Source
BITACM2018第一轮积分赛(三)- Problem J
思路:22-地下城与勇士】
【解题思路】
建议零基础的同学在做此题前先研究一下一般的迷宫怎么搜索路径。

本题和普通的迷宫搜路径只有一个区别,就是有的障碍(也就是怪物)会在某些时间点消失。那么应该怎么处理?其实这个问题的本质就是k,2k,3k,……时的地图和其他时间点是不一样的。所以实际上我们可以这么理解,我们把时间看成一个维度,可以把时间这个维度想象成高度,我们每走一步,实际上不仅是在这个二维地图上移动了一格,同时还在三维的高度上我们向上移动了一层,如同一个魔塔一样。只不过这个塔,第k,2k,……层是一个地图,其他层是另一个地图。当然,我们考虑到,我们t秒时在某个地点,和k+t秒时在某个地点,实际上是等价的。所以,我们可以把这个塔的结构优化成k层,k层再往上相当于又回到了1层。最终,我们的任务就变成了想办法移动到任意一层的终点位置。

【代码细节】
我们按照这样的思路,与原来唯一的区别就是,我们用于记录遍历情况的vis数组由二维变成了三维,vis[i1][i2][i3]表示在i3的时间(也可能是k+i3,2k+i3,……)遍历过(i1,i2)这个位置,无需重复遍历。借助这个vis数组bfs,我们就能找到通往终点的最短路径。

【坑点】
别忘了判断输出-1的情况,可以一开始给dis全赋值INF=0x3f3f3f3f,用memset(dis,0x3f,sizeof(dis));即可实现该操作,这样如果最后终点距离是INF,那么输出-1

代码:`#include
#include
#include
#include //用memeset得用这个库
using namespace std;
const int mx[]={0,0,1,-1};
const int my[]={1,-1,0,0};

struct NODE{
int x,y,z;
int depth;
NODE(int _x,int _y,int _z,int _depth):x(_x),y(_y),z(_z),depth(_depth){}
};
queue q;
char map[110][110];
int jdg[110][110][55];
int n,m,k,T;
int begin_x,begin_y;
void getmap(){
scanf("%d %d %d\n",&n,&m,&k);
for(int j=0;j<n;j++){
scanf("%s",&map[j]);
for(int c=0;c<m;c++){
if(map[j][c]==‘S’){
begin_x=j;
begin_y=c;
}
}
}
}

int main(){
scanf("%d\n",&T);
memset(jdg, 0, sizeof(jdg));
while(T–){
while(!q.empty())
q.pop();
getmap();
memset(jdg, 0, sizeof(jdg));
// int jdg[105][105][55];//初始化
q.push(NODE(begin_x,begin_y,0,0));
jdg[begin_x][begin_y][0]=1;
int count;
int f=0;
while(!q.empty()){
NODE temp=q.front();
q.pop();
if(map[temp.x][temp.y]==‘E’){
count=temp.depth;
f=1;
break;
}
//上下左右分别尝试

		for(int j=0;j<4;j++){
			int x=temp.x+mx[j];
			int y=temp.y+my[j];
			if((temp.z+1)%k==0){
				 if (x < 0 || x >= n || y < 0 || y >= m || map[x][y] == '#'||jdg[x][y][(temp.z+1)%k] == 1)
                
				continue;
			}
			else{
				if (x < 0 || x >= n || y < 0 || y >= m || map[x][y] == '#' || map[x][y]=='*'||jdg[x][y][(temp.z+1)%k] == 1)
                continue;
                
			}

//if((temp.z+1)%k)//此层有怪
// {
// if (x < 0 || x >= n || y < 0 || y >= m || map[x][y] == ‘#’ || map[x][y]==’*’||jdg[x][y][(temp.z+1)%k] == 1)
// continue;
// }
// else//此层无怪
// {
// if (x < 0 || x >= n || y < 0 || y >= m || map[x][y] == ‘#’||jdg[x][y][(temp.z+1)%k] == 1)
// continue;
// }

			q.push(NODE(x,y,temp.z+1,temp.depth+1));
			jdg[x][y][(temp.z+1)%k]=1;
		}
		
	}
	if(f==1)
		printf("%d\n",count);
	else
		printf("-1\n");
}
return 0;

}

//#include
//#include
//#include
//#include
//using namespace std;
//
//const int mx[] = {0, 0, 1, -1};
//const int my[] = {1, -1, 0, 0};//上下右左
//
//struct NODE {
// int x, y, z;
// int depth;
// // 初始化列表
// NODE(int _x, int _y,int _z, int _depth) : x(_x), y(_y), z(_z), depth(_depth){}
//};
//queue q;
//int jdg[110][110][55];
//char map[110][110];
// int n, m, T, k,temp;
// int start_x, start_y;
// void getmap(){
// for (int i = 0; i < n; ++i) {
// scanf("%s", &map[i]);
// for (int j = 0; j < m; ++j) {
// if (map[i][j] == ‘S’) {
// start_x = i;
// start_y = j;
// }
// }
// }
// }
// void kong(){
//
// }
// void kong1(){
//
// }
//int main() {
//
// scanf("%d", &T);
// while(T–)
// {
// while(!q.empty())
// q.pop();
// scanf("%d%d%d", &n,&m,&k);
// getmap();
// // 又是一套初始化
// memset(jdg,0,sizeof(jdg));
// q.push(NODE(start_x, start_y, 0, 0));//把括号内的三个作为三个参数传递进去,把一个node型数据放进队列
// jdg[start_x][start_y][0] = 1;
// int f = 0;
// int count=0;
// while (!q.empty()) {
// NODE temp = q.front();//tmp为取出来的待处理的队首struct node类型的元素
// q.pop();
// if (map[temp.x][temp.y] == ‘E’) {
// count=temp.depth;
// f = 1;
// break;
// }
// kong1();
// kong();
// for(int j=0;j<4;j++){
// int x=temp.x+mx[j];
// int y=temp.y+my[j];
// if((temp.z+1)%k0){
// if (x < 0 || x >= n || y < 0 || y >= m || map[x][y] == ‘#’||jdg[x][y][(temp.z+1)%k] == 1)
//
// continue;
// }
// else{
// if (x < 0 || x >= n || y < 0 || y >= m || map[x][y] == ‘#’ || map[x][y]
’*’||jdg[x][y][(temp.z+1)%k] == 1)
// continue;
//
// }
//
// q.push(NODE(x, y, temp.z+1, temp.depth + 1));
// jdg[x][y][(temp.z+1)%k] = 1;
// }
// }
// if(f==1)
// printf("%d\n", count);
// else
// printf("-1\n");
//
// }
//
//
// return 0;
//}`

旋转数独

Description
数独是一个基于逻辑的组合数字放置拼图,在世界各地都很受欢迎。
在这个问题上,让我们关注 16 乘號 16 网格的拼图,其中包含 4 乘號 4 个区域。 目标是用十六进制数字填充整个网格,即 0123456789 A B C D E F,以便每列,每行和每个区域包含所有十六进制数字。

下图显示了一个被成功解决的数独例子:



昨天,周老师解决了一个数独并将其留在桌面上。 然而,龙龙想和他开个玩笑——龙龙打算对这个已经解决的数独进行多次以下操作。

 选择一个  4 乘號 4 的小区域并顺时针旋转 90 度。
周老师回来发现他拼好的数独板被打乱了,开始挠头,你能帮他以最小的步数恢复原样吗?请你手把手的教他怎么做,也就是需要输出方案。

请注意选择要旋转的方块不能跨越任何小区域,也就是说必须选择一块完整的小区域旋转。小区域的定义在上面,16 乘號 16 的网格被分成 4 乘號 4 个小区域。

Input
第一行输入一个正整数 T 空格 左括号 1 小於等於 T 小於等於 10 立方 右括号 表示数据组数;
接下来每组数据输入一个 16 乘號 16 的数独图,表示被龙龙打乱后的数独面板。

Output
对于每组数据:

第一行输出一个整数 a n s ,表示周老师最少需要逆时针旋转多少次才能恢复原样。

接下来输出 a n s 行,每行两个数p 下標 x 空格 空格 空格 p 下標 y,表示逆时针旋转一次第p 下標 x行第p 下標 y列的小矩阵。
23-带旋转的数独游戏】
【解题思路】
本题题意是:原本一个做好的16*16数独,做了多次基于单元格的旋转,问如何恢复这个数独。提前计算复杂度,我们可以考虑到,每个单元格有4种转动方案(转几次的问题),一共有16个单元格,也就是4^16种情况,如果把所有情况遍历一遍时间复杂度肯定会炸。因此,本题的关键在于找到一个好的剪枝策略,难点在于写出有条理的代码。

我们注意到,其实这种旋转的数独有个特点,就是最后的方案其实只有两种情况,一种是将数独恢复成原本的样子,另一种是在原本样子的基础上每个单元格转2次,除此之外别无情况。也就是说,其实每个大行也就是2种方案。因此,我们可以考虑这样做:先仅维护每个大行满足数独条件,这样每个大行复杂度是4^4,维护好以后,无非就是保持这样还是每个都再多转少转2次的问题。这样,4个大行复杂度就是4*4^4,并不会炸。

维护好每个大行之后,我们再去看列的情况,实际上,无论是维护大行还是大列,我们仅看其中的一行或一列即可,想想为什么?看列,我们也不需要4个大列都看,因为其实每个大行的4个大列转法已经被绑定了。仅看第一列即可,对于每一行,有2种转法,其实就是保持原样还是多转2次,这样复杂度是2^4。这样,大列也被维护好了。

最后选择最优答案,我们只需计算出当前方案的转动次数,再计算每个单元格多转或少转2次的转动次数,两者相比较,输出较优的方案即可,最坏复杂度为O(4*4^4+2^4)。

【代码细节】
可以使用一个4*4的二维数组,存储转动方案,你可以在程序中使得一个地方转动次数超过4,没关系,最后处理的时候对4取模即可。

本题写代码可能对很多同学是个难题,在此给出我的建议。

将每种操作都写成函数封装起来,一个子任务一个子任务的完成。

init():初始化。

CreateMap():将字符地图读入,建议转成数字形式储存,便于之后判断方案的正确性。

rotate(x,y):转动第x大行第y大列的单元格1次,这里要好好推公式,不过大一上C语言课应该做过这类题。

check_row(x):检查第x行是否符合要求,即检查一行中是否1-16均出现过即可。

check_col(y):检查第y列是否符合要求,同上

dfs_row(x,y):用于维护大行的dfs,表示dfs到x大行y大列

dfs_col(x,y):用于维护大列的dfs,表示dfs到x大行y大列(其实在我的程序里,y一直是1,不过为了与dfs_row对应,也写成了这样)

print():输出最优方案



【坑点】
dfs最容易出错的地方就是回溯,你可以通过转4次当做回溯,也可以在转动之前存储一个副本,回溯时通过副本还原回来即可。

由于是多组数据,所以记得做好初始化。
#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

char map1[18][18],map[18][18];
int squarei[4][4];
char squarec[4][4];
int c[16];
char x[16];//列的判断 
int f=0,T;
int judge_line(int x);
void dfs(int x, int y);
void roll(int n, int x, int y);
void reset(int x, int y);
void judge_colomn();


int judge_line(int x){//  记得清空 
	for(int i=0;i<16;i++){
		switch(map[x][i]){
            case '0': c[0]++; break;
            case '1': c[1]++; break;
            case '2': c[2]++; break;
            case '3': c[3]++; break;
            case '4': c[4]++; break;
            case '5': c[5]++; break;
            case '6': c[6]++; break;
            case '7': c[7]++; break;
            case '8': c[8]++; break;
            case '9': c[9]++; break;
            case 'A': c[10]++; break;
            case 'B': c[11]++; break;
            case 'C': c[12]++; break;
            case 'D': c[13]++; break;
            case 'E': c[14]++; break;
            case 'F': c[15]++; break;
        }
	}
	for(int i=0;i<16;i++)
		if(c[i]==0||c[i]>1){
			memset(c, 0, sizeof(c));
			return 0;
		}
	memset(c, 0, sizeof(c));
	return 1;
}
int judge_column(){
	for (int i = 0; i < 16; i++){
        switch(x[i]){
            case '0': c[0]++; break;
            case '1': c[1]++; break;
            case '2': c[2]++; break;
            case '3': c[3]++; break;
            case '4': c[4]++; break;
            case '5': c[5]++; break;
            case '6': c[6]++; break;
            case '7': c[7]++; break;
            case '8': c[8]++; break;
            case '9': c[9]++; break;
            case 'A': c[10]++; break;
            case 'B': c[11]++; break;
            case 'C': c[12]++; break;
            case 'D': c[13]++; break;
            case 'E': c[14]++; break;
            case 'F': c[15]++; break;
        }
    }
    for(int i = 0; i < 16; i++){
        if(c[i] == 0 || c[i] > 1){
        	memset(c, 0, sizeof(c));
            return 0;
        }
    }
    memset(c, 0, sizeof(c));
    return 1;
}

void roll(int n,int x,int y){//n represents the number of rotation
	for(int i=0;i<n;i++){
		for(int j=3;j>=0;j--){
			for(int k=0;k<4;k++)
				squarec[3-j][k]=map[k+4*x-4][j+4*y-4];
		}
		for(int j1=0;j1<4;j1++){
			for(int k1=0;k1<4;k1++)
				map[j1+4*x-4][k1+4*y-4]=squarec[j1][k1];
		}
	}
	squarei[x-1][y-1]=n;
}
void reset(int x,int y){
	for(int i=0;i<4;i++){
		for(int j=0;j<4;j++){
			map[4*x-4+i][4*y-4+j]=map1[4*x-4+i][4*y-4+j];	
		}
	}
}
void dfs(int x,int y){
	if(f==1)
		return;
    if(y == 4){
        for(int i = 0; i < 4; i++){
            roll(i,x,y);
            if(judge_line((x - 1)* 4) == 1){
                f = 1;
                return;
            }
            else{
                reset(x, y);
                squarei[x - 1][y - 1] = 0;
            }
        }
    }
    else{
        for (int i = 0; i < 4; i++){
            roll(i,x,y);
            dfs(x, y + 1);
            if(judge_line((x-1)*4)==1){
                f=1;
				return;
            }
            else{
                reset(x,y);
            }
        }
    }	
}
int main(){
	scanf("%d\n",&T);
	while(T--){
		f=0;
		for(int h=0;h<16;h++){
			scanf("%s",&map[h]);
			for(int l=0;l<16;l++)
				map1[h][l]=map[h][l];
		}
		for(int h=1;h<5;h++){
			f=0;
			dfs(h,1);
		}
		f=0;
		for (int i1 = 0; i1 < 2; i1++)
        {
           
            for (int i2 = 0; i2 < 2; i2++)
            {
               
                for (int i3 = 0; i3 < 2; i3++)
                {
                    
                    for (int i4 = 0; i4 < 2; i4++)
                    {   
                        x[0] = map[0][0+i1*3];
                        x[1] = map[1][0+i1*3];
                        x[2] = map[2][0+i1*3];
                        x[3] = map[3][0+i1*3];
                        x[4] = map[4][0+i2*3];
                        x[5] = map[5][0+i2*3];
                        x[6] = map[6][0+i2*3];
                        x[7] = map[7][0+i2*3];
                        x[8] = map[8][0+i3*3];
                        x[9] = map[9][0+i3*3];
                        x[10] = map[10][0+i3*3];
                        x[11] = map[11][0+i3*3];
                        x[12] = map[12][0+i4*3];
                        x[13] = map[13][0+i4*3];
                        x[14] = map[14][0+i4*3];
                        x[15] = map[15][0+i4*3];
                        if(judge_column() == 1)
                            f=1;
                        if(f)
                        	break;
                        for (int m = 0; m < 4; m++){
                                squarei[3][m] = (squarei[3][m] + 2) % 4;
                            }
                    }
                    if(f)
                       	break;
                    for (int m = 0; m < 4; m++){
                                squarei[2][m] = (squarei[2][m] + 2) % 4;
                            }
                }
                if(f)
                   	break;
                for (int m = 0; m < 4; m++){
                                squarei[1][m] = (squarei[1][m] + 2) % 4;
                            }
            }
            if(f)
               	break;
            for (int m = 0; m < 4; m++){
                                squarei[0][m] = (squarei[0][m] + 2) % 4;
                            }
        }
 

		 int total = 0, total1 = 0;
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 4; j++)
            {
                total = squarei[i][j] + total;
                total1 = (squarei[i][j] + 2) % 4 + total1;
            }
        if (total < total1)
        {
            printf("%d\n", total);
            for (int i = 0; i < 4; i++)
                for (int j = 0; j < 4; j++)
                    for (int k = 0; k < squarei[i][j]; k++)
                        printf("%d %d\n", i + 1, j + 1);
        }
        else
        {
            printf("%d\n", total1);
            for (int i = 0; i < 4; i++)
                for (int j = 0; j < 4; j++)
                    for (int k = 0; k < (squarei[i][j] + 2) % 4; k++)
                        printf("%d %d\n", i + 1, j + 1);
        }			
	}
	return 0;
}

#逻辑闭环
24. 逻辑闭环
成绩 10 开启时间 2022年08月29日 星期一 11:30
折扣 0.8 折扣时间 2022年09月10日 星期六 23:59
允许迟交 否 关闭时间 2022年10月10日 星期一 23:59
Description
小张是一位推理迷,他非常喜欢看侦探小说与侦探电影。同时他也会玩一些推理游戏,在侦探游戏中,小张需要发掘事件之间的联系。通过一条线索,他能够通过事件A推理出事件B。如果通过某一个事件A能够推出事件A本身,那么他就能够完成推理。现在按照顺序给出 m 条线索,请你算出他最少能够用前几条线索能够形成逻辑闭环完成推理。
Input
第一行 n,m(1 \leq n,m \leq 300000 ) 两个数,表示事件数和线索数。
接下来 m 行,每行两个数 A,B(1 \leq A,B \leq n ) ,表示事件A能够推出事件B。

Output
一行一个数,表示最少能够用前几条线索形成逻辑闭环完成推理。若无法完成输出-1。
题解:
【24-逻辑闭环】
【解题思路】
注意到如果前i-1条线索不能形成闭环,而第i条恰好可以形成逻辑闭环,那么第i条边一定在这个闭环里,因此每加入一条新边,就从新边开始bfs,寻找闭环即可,注意不是最短路问题,所以不必开vis数组,只要保证不往回走即可。(其实在笔者在这样AC以后,考虑到极端数据实际可以把这种做法卡掉,比如图是一个长链结构,算法复杂度会退化成O(n^2),因此如果要做出真正的正解,笔者认为可以采用二分查找的方法,可以达到O(nlogn)的复杂度)

【代码细节&坑点】
本题时间卡得比较紧,建议使用scanf/printf输入输出,一些小型函数可以加个inline节省一下时间。

代码:`#include
#include
#include
using namespace std;

struct junction{
int end;
int judge;
junction(int end_,int judge_):end(end_),judge(judge_){}
};

struct time{
int begin;
int end;
};
vector g[400000];
queue q;
int dfs(int a,int b){
q.push(b);
while(!q.empty()){
int temp=q.front();
for(int i=0;i<g[temp].size();i++){
if(g[temp][i].judge0){
if(g[temp][i].end
a){
return 1;
}
g[temp][i].judge=1;
q.push(g[temp][i].end);
}
}
q.pop();
}
return 0;
}

int main(){
int n,m;
scanf("%d %d\n",&n,&m);
for(int i=0;i<m;i++){
struct time temp;
scanf("%d %d\n",&temp.begin,&temp.end);
g[temp.begin].push_back(junction(temp.end,0));
int flag=dfs(temp.begin,temp.end);
if(flag){
printf("%d\n",i+1);
return 0;
}
}
printf("-1\n");
return 0;
}`
#进圈
Description
龙龙最近迷上了一款名叫 PUBG(PLAYERUNKNOWN’S BATTLEGROUNDS)的手游,那是一款关乎生存挑战的 RPG 逃亡游戏。

考虑到游戏的环节过于复杂,龙龙决定简化一下场景:整个地图可以看做一个长为 ​ 宽为 ​ 的二维格点平面。龙龙需要从 左括号 x 下標 1 逗號 空格 y 下標 1 右括号 逃亡到 左括号 x 下標 2 逗號 空格 y 下標 2 右括号 以逃离毒圈,但有些格点上存在障碍#不能行走,有些格点是沙地.。龙龙只能移动在允许行走的沙地上,同时每一时刻,龙龙只能朝着当前位置周围的上、下、左、右四个方向移动。同时因为龙龙使用了能量饮料,每分钟最多可以朝着一个方向行走 k 步。

毒圈快要来啦,请你帮龙龙尽快安排一下可行的路线,使得它能够以最短的时间顺利进圈。

Input
第一行输入三个正整数 n 逗號 空格 m 和 k 空格 左括号 1 小於等於 n 逗號 空格 m 逗號 空格 k 小於等於 1000 右括号 表示地图的大小还有龙龙每分钟最多可以移动的步数;

接下来 n 行,每行包含 m 个字符,其中第 i 行第 j 个字符表示坐标 左括号 i 逗號 空格 j 右括号 的路况,它可能是#,这表示这个格点是障碍区,不能行走,也可能是.表示沙地;

最后一行输入四个正整数 x 下標 1 逗號 空格 y 下標 1 逗號 空格 x 下標 2 逗號 空格 y 下標 2 由空格间隔开,表示龙龙的初始位置和目标位置。

Output
请输出一个正整数 t,表示龙龙从 左括号 x 下標 1 逗號 空格 y 下標 1 右括号 到 左括号 x 下標 2 逗號 空格 y 下標 2 右括号 进圈最少需要的时间(分钟),如果龙龙最终不能进圈,则请输出-1。

Hint
对于样例,龙龙第一分钟走 4 步,从 左括号 1 逗號 空格 3 右括号 到 左括号 1 逗號 空格 1 右括号,第二分钟从 左括号 1 逗號 空格 1 右括号 到 左括号 3 逗號 空格 1 右括号,第三分钟从 左括号 3 逗號 空格 1 右括号 走到 左括号 3 逗號 空格 3 右括号 顺利进圈。
题解:
【解题思路】
我们还是考虑这跟一般的迷宫搜路径有什么区别。区别在于:原来走一步能到的位置最多只有相邻4格,而现在走一步能到的位置最多可以有4k格,除此之外没有任何区别。因此,我们只需把之前bfs的入队方式改一下:一个方向的k格依次入队,直到碰到障碍break,四个方向各做一次这个操作。

【代码细节】
一般这种路径bfs,我们在转移时,都会用到两个数组int dx[4]={1,0,-1,0}, dy[4]={0,1,0,-1};转移的时候会写成xx=x+dx[i],yy=y+dy[i],现在我们只需要改成xx=x+dx[i]*j,yy=y+dy[i]*j即可。

【坑点】
注意一个方向走k格的过程中,如果遇到障碍,要做的是break,也就是在这个方向停止探索,而不是continue,这样的效果是越过障碍继续向前移动。
代码:

#include <cstdio>  
#include <cstring>  
#include <queue>   
#include <algorithm>  
using namespace std; 
const int dx[]={1,-1,0,0};
const int dy[]={0,0,1,-1};
struct node{
	int x,y,step;
	node(int x_,int y_,int step_):x(x_),y(y_),step(step_){
	}
};

int n,m,k,i,j,flag=0,total;
int begin_x,begin_y,end_x,end_y;
char map[1001][1001];
int  judge[1001][1001];
queue<node> q;
int main(){
	memset(map,0,sizeof(map));
	scanf("%d %d %d\n",&n,&m,&k);
	for(i=0;i<n;i++){
		scanf("%s",&map[i]);   //不用加 \n   
	}
	scanf("%d %d %d %d",&begin_x,&begin_y,&end_x,&end_y);
	q.push(node(begin_x-1,begin_y-1,0));
	judge[begin_x-1][begin_y-1]=1;
	memset(judge,0,sizeof(judge));
	
	while(!q.empty()){
		node temp=q.front();
		q.pop();
		for(i=0;i<4;i++){
			for(j=1;j<k+1;j++){
			int x=temp.x+dx[i]*j;
			int y=temp.y+dy[i]*j;	
				if(x<0||y<0||x>=n||y>=m||map[x][y]=='#')  break;  
                  
                if(judge[x][y]==1) continue; //等于1表示已经过  为什么直接continue 
                if(x==end_x-1&&y==end_y-1){
                	flag=1;
                	total=temp.step+1;//想清为什么加一 
				}
				if(flag==1)
					break;
				q.push(node(x,y,temp.step+1));
				judge[x][y]=1;
			}
			if(flag==1)
				break;
		}
		if(flag==1)
			break;
	}
	if(flag==1)
		printf("%d\n",total);
	else
		printf("-1\n");
	return 0;
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值