Luogu P1374
刚刚翻题解区发现我的做法和一些神犇有亿点相似…?不会是老师给我们讲题之前看过题解吧…(老师您可千万别看见我
这道题是在地图上的操作,四个方向,因此大概率是搜索。求的是和魔王相遇所需的最短时间,刚好和广搜十分吻合。如果对广搜还有疑惑,建议先去看看p1443,是很好的广搜模板题。
基本思路很简单,模拟萨尔和魔王的位置变化,同时循环模拟出小A所有可能的移动,当小A与魔王位置相同时,输出总时间。
基本地,用bool类型的mark数组存储是否走过的信息,小A每次移动后都要标记走过。用char类型的map数组存储地图,因为输入的01矩阵没有空格,如果用int类型的话每行就会被存成一个整数。
额外的,表示萨尔和魔王移动的数字串要用字符数组或字符串来存储,原因同上,同时在取第i位时要注意将字符类型转化为整数类型。
首先定义一个结构体info,存储3人的基本信息
struct info
{
int a_x, a_y;//小A的位置
int s_x, s_y;//萨尔的位置
int devil_x, devil_y;//魔王的位置
int t, t1;//总秒数, 超出光环的秒数
};
如果在光环内,就把t1赋值为0,否则就在上一次移动的基础上+1
然后用两个函数分别模拟萨尔和魔王的移动。注意,如果数字串当前位为0就原地不动,如果移动后会超出边界也要原地不动。
info master_move(info temp, int master){
if(master == 0)return temp;//当前数字是0,原地不动
if(master == 1){//向上走
int xx = temp.s_x - 1;
int yy = temp.s_y;
if(!check(xx, yy))return temp;
temp.s_x = xx;
return temp;
}
else if(master == 2){//向下走
int xx = temp.s_x + 1;
int yy = temp.s_y;
if(!check(xx, yy))return temp;
temp.s_x = xx;
return temp;
}
else if(master == 3){//向左走
int xx = temp.s_x;
int yy = temp.s_y - 1;
if(!check(xx, yy))return temp;
temp.s_y = yy;
return temp;
}
else {//向右走
int xx = temp.s_x;
int yy = temp.s_y + 1;
if(!check(xx, yy))return temp;
temp.s_y = yy;
return temp;
}
}
魔王和萨尔的函数几乎相同,在此不多赘述,只是要注意变量名的变化。
AC代码
#include <iostream>
#include <cstdio>
#include <string>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
int n, m, s, d;//n * m, 超出光环的秒数, 光环笼罩的范围
int _x, _y, x2, y2;
bool mark[55][55];
//方向数组
int cx[5] = {0, 1, -1, 0, 0};
int cy[5] = {0, 0, 0, 1, -1};
string sal, devil;
struct info
{
int a_x, a_y;//小A的位置
int s_x, s_y;//萨尔的位置
int devil_x, devil_y;//魔王的位置
int t, t1;//总秒数, 超出光环的秒数
};
queue <info> line;
char map[55][55];
double howfar(info m){//小A和萨尔之间的距离
double x = sqrt(pow(m.a_x - m.s_x, 2) + pow(m.a_y - m.s_y, 2));//距离公式
return x;
}
bool check(int x, int y){
//如果这个点超出了边界,或者已经走过,或者是障碍,那么就不可以走
if(x > n || x <= 0 || y > m || y <= 0 || mark[x][y] == 1 || map[x][y] == '1')return 0;
return 1;
}
info master_move(info temp, int master){
if(master == 0)return temp;//当前数字是0,原地不动
if(master == 1){//向上走
int xx = temp.s_x - 1;
int yy = temp.s_y;
if(!check(xx, yy))return temp;
temp.s_x = xx;
return temp;
}
else if(master == 2){//向下走
int xx = temp.s_x + 1;
int yy = temp.s_y;
if(!check(xx, yy))return temp;
temp.s_x = xx;
return temp;
}
else if(master == 3){//向左走
int xx = temp.s_x;
int yy = temp.s_y - 1;
if(!check(xx, yy))return temp;
temp.s_y = yy;
return temp;
}
else {//向右走
int xx = temp.s_x;
int yy = temp.s_y + 1;
if(!check(xx, yy))return temp;
temp.s_y = yy;
return temp;
}
}
info lord_move(info temp, int lord){
if(!lord)return temp;//当前数字是0,原地不动
if(lord == 1){//向上走
int xx = temp.devil_x - 1;
int yy = temp.devil_y;
if(!check(xx, yy))return temp;
temp.devil_x = xx;
return temp;
}
else if(lord == 2){//向下走
int xx = temp.devil_x + 1;
int yy = temp.devil_y;
if(!check(xx, yy))return temp;
temp.devil_x = xx;
return temp;
}
else if(lord == 3){//向左走
int xx = temp.devil_x;
int yy = temp.devil_y - 1;
if(!check(xx, yy))return temp;
temp.devil_y = yy;
return temp;
}
else {//向右走
int xx = temp.devil_x;
int yy = temp.devil_y + 1;
if(!check(xx, yy))return temp;
temp.devil_y = yy;
return temp;
}
}
int main(){
freopen("code.in", "r", stdin);
freopen("code.out", "w", stdout);
cin >> n >> m >> s >> d;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++){
cin >> map[i][j];
}
cin >> _x >> _y >> x2 >> y2;
mark[_x][_y] = true;
getline(cin, sal);//getline会读入上一行的回车,所以要用两次getline覆盖
getline(cin, sal);
getline(cin, devil);
info point;
point.a_x = point.s_x = _x;
point.a_y = point.s_y = _y;
point.devil_x = x2;
point.devil_y = y2;
point.t = point.t1 = 0;
line.push(point);
while(!line.empty()){
info point = line.front();
line.pop();
point.t++;//总时间+1
int master, lord;
//字符串中第t位,并转为整数类型
master = sal[(point.t - 1) % sal.size()] - '0';
lord = devil[(point.t - 1) % devil.size()] - '0';
info now;
now = master_move(point, master);//更新萨拉的位置
now = lord_move(point, lord);//更新魔王的位置
//魔王移动后判断小A与魔王的相对位置
if(now.a_x == now.devil_x && now.a_y == now.devil_y){
cout << now.t << endl;
return 0;
}
//小A开始移动
for(int i = 1; i <= 4; i++){//小A可以向四个方向移动,每次一个单位长度
//移动后坐标
int xx = point.a_x + cx[i];
int yy = point.a_y + cy[i];
if(check(xx, yy)){//可以移动到这个坐标
now.a_x = xx;
now.a_y = yy;
if(howfar(now) <= d){//在光圈内
info next = now;
next.t1 = 0;//超出光圈的时间清零
mark[xx][yy] = 1;//标记走过
line.push(next);
if(now.a_x == now.devil_x && now.a_y == now.devil_y){//如果小A遇到魔王
cout << now.t << endl;
return 0;
}
}
else if(now.t > s){//超出存活时间,不可继续
continue;
}
else {//不在光圈内,但是仍在存活时间内。仍可添加新节点
info next = now;
next.t1 = point.t1 + 1;//光圈外活动时间+1
line.push(next);
mark[xx][yy] = 1;
if(now.a_x == now.devil_x && now.a_y == now.devil_y){//如果小A遇到魔王
cout << now.t << endl;
return 0;
}
}
}
}
}
return 0;
}