0
点赞
收藏
分享

微信扫一扫

ACWING/1986. 镜子

sin信仰 2022-05-02 阅读 42

在这里插入图片描述

在这里插入图片描述

输入样例:

5 6 2
3 0 /
0 2 /
1 2 /
3 2 \
1 3 \

输出样例:

4

样例解释

农场的平面图如下所示,其中

H 表示约翰的房子,

B 表示牛棚:

3 .\.....
2 //.\..B
1 .......
0 H../...
  0123456

通过改变 (3,2)处的围栏朝向,就可以使约翰看到点 (a,b),如下所示:

3 .\.....
2 //./--B
1 ...|...
0 H--/...
  0123456
3 .\.....
2 //./--B
1 ...|...
0 H--/...
  0123456

题意解读:

可以理解为从原点(0,0)开始向右发射一束光,通过镜子/ \两种45°倾斜的镜子反射光,则光可以90°的角度变化到上下左右另一方向继续射出,最终需要射到终点(a,b)。

最多改变一个镜子能达到如上目标则输出改变镜子的编号,否则输出-1,不改变镜子则输出0.

思路:枚举

  • 将不翻转时和每次翻转第i个镜子的路径遍历一遍看是否走到终点,需要n+1次

  • 遍历路径方法:将镜子视为节点,从当前原点节点出发,每次当前节点都需要找到下一个镜子节点(即该镜子在当前方向上,且距离最近),方法为遍历所有的镜子节点,查看两个节点构成的向量的模乘上当前目标方向得到一个向量,再加当前节点,查看是否等于下一个节点。直到找到终点或者找不到终点。

  • 每一个路径遍历时会出现三种情况:

    • 1.如果走了(2n+1)个镜子节点了还未到终点,则一定是进入了循环到不了终点(注:2n+1是所有镜子形成循环即最大的循环需要的节点数)
  • 2.如果在当前节点找不到下一个镜子节点,则说明此方向没有镜子了,一定到不了终点

  • 3.到达终点

  • 由上可知路径遍历需要n^2时间复杂度

  • 所以枚举算法需要n^3的时间复杂度,因为n<200,故可以接受

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <vector>
#include <algorithm>

#define INF 0x3f3f3f3f

using namespace std;

const int N = 210;
int n;

// 定义方向,上 右 下 左
int dx[] = {0,1,0,-1};
int dy[] = {1,0,-1,0};

struct Node{
        int x,y;
        char mirror;
}M[N];

void change_mirror(Node &node){
        // 翻转镜子
        if(node.mirror == '\\') node.mirror = '/';
        else node.mirror = '\\';
}

bool check(){
        // 遍历当前的镜子节点路径是否能到达终点
        int d = 1; // 方向向右
        int index = 0; // 当前的镜子
        M[0].x = M[0].y = 0; // 初始化开始位置为原点
        for(int i = 0;i<(2*n+1);i++){
                // 遍历最多(2*n+1),则一定是陷入循环了,不可能到达终点
                int next = -1,len = INF; // 下一个节点和下一个节点到此节点的距离
                for(int j = 1;j<=n+1;j++){
                        if(index == j) continue; // 相同节点直接跳过
                        // 当前节点 加 当前方向乘模长 需要等于 目标节点
                        if(M[index].x + abs(M[index].x - M[j].x) * dx[d] != M[j].x) continue;
                        if(M[index].y + abs(M[index].y - M[j].y) * dy[d] != M[j].y) continue;
                        // 找到最近的那个节点
                        int length = abs(M[index].x - M[j].x) + abs(M[index].y - M[j].y);
                        if(length < len) len = length, next = j;
                }
                if(next == -1) return false; // 找不到下一个节点
                if(next == n+1) return true; // 找到了目标节点
                // 根据下一个节点的镜子更换方向
                index = next;
                if(M[next].mirror == '/'){
                        if(d == 0) d = 1;
                        else if(d == 1) d = 0;
                        else if(d == 2) d = 3;
                        else d = 2;
                }else{
                        if(d == 0) d = 3;
                        else if(d == 1) d = 2;
                        else if(d == 2) d = 1;
                        else d = 0;
                }
        }
        return false;
}

int main(){
        /* 思路:枚举
        * 将每一个翻转i镜子的路径和所有都不翻转遍历一遍看是否走到终点,需要n+1次
        * 遍历路径方法:将镜子视为节点,从当前原点节点出发,每次当前节点都需要找到下一个镜子节点(即该镜子在当前方向上,且距离最近),方法为遍历所有的镜子节点,查看两个节点构成的向量的模乘上当前目标方向得到一个向量,再加当前节点,查看是否等于下一个节点。直到找到终点或者找不到终点。
        * 每一个路径遍历时会出现三种情况:
        *        1.如果走了(2n+1)个镜子节点了还未到终点,则一定是进入了循环到不了终点
        *        2.如果找不到下一个节点,则到不了终点
        *        3.到达终点
        *        由上可知路径遍历需要n^2时间复杂度
        * 所以枚举算法需要n^3的时间复杂度,因为n<200,故可以接受
        * */
        cin>>n;
        cin>>M[n+1].x>>M[n+1].y; // n+1的位置存目标
        for(int i = 1;i<=n;i++) cin>>M[i].x>>M[i].y>>M[i].mirror;
        
        if(check()){
            cout<<0<<endl;
            return 0;
        } 
        else{
                for(int i = 1;i<=n;i++){
                        change_mirror(M[i]); // 翻转镜子
                        if(check()){
                                cout<<i<<endl;
                                return 0;
                        }
                        change_mirror(M[i]); // 改变回来
                }
        }
        cout<<-1<<endl;
        return 0;
}
举报

相关推荐

0 条评论