# P2937 [USACO09JAN] Laserphones S

P2937 [USACO09JAN] Laserphones S

题目描述

奶牛们有一个新的激光系统,这样它们在牧场上时可以进行随意的交谈。牧场被建模为一个 $W \times H$ 的点阵($1 \leq W \leq 100$,$1 \leq H \leq 100$)。

该系统需要某种视线连通性以维持通信。当然,牧场上有岩石和树木会干扰通信,但奶牛们购买了对角镜(如下的 '/' 和 '\')来使激光束偏转 90 度。下面是一个说明问题的地图。

对于这张地图,$H$ 是 8,$W$ 是 7。两个正在通信的奶牛用 'C' 表示;岩石和其他阻挡元素用 '*' 表示:

7 . . . . . . .         7 . . . . . . . 
6 . . . . . . C         6 . . . . . /-C 
5 . . . . . . *         5 . . . . . | * 
4 * * * * * . *         4 * * * * * | * 
3 . . . . * . .         3 . . . . * | . 
2 . . . . * . .         2 . . . . * | . 
1 . C . . * . .         1 . C . . * | . 
0 . . . . . . .         0 . \-------/ . 
0 1 2 3 4 5 6           0 1 2 3 4 5 6 

确定必须安装的最少镜子数量 $M$,以维持两头奶牛之间的激光通信。在给定的测试数据中,这一壮举总是可能的。

输入格式

* 第 1 行:两个用空格分隔的整数:$W$ 和 $H$

* 第 2 行到第 $H+1$ 行:整个牧场。

输出格式

* 第 1 行:一个整数:$M$

输入输出样例 #1

输入 #1

7 8 
....... 
......C 
......* 
*****.* 
....*.. 
....*.. 
.C..*.. 
.......

输出 #1

3

说明/提示

(由 ChatGPT 4o 翻译)

题解
本题的关键就在于求出最短路径的中的转向次数,题目让我们求镜子的最小值,每次转向就意味着需要一枚镜子
首先我们在定义结构体时在有方向的基础上加入turn即(转向的次数)即可

点击查看代码
typedef struct{
    int x,y;
    int dir;
    int turn;
}Node;
这是一整个操作其中
点击查看代码
while (front < rear) {
        Node now = queue[front++];
        int dir = now.dir;

        // 向当前方向继续移动
        int nx = now.x + dx[dir];
        int ny = now.y + dy[dir];

        if (nx >= 0 && nx < h && ny >= 0 && ny < w && map[nx][ny] != '*') {
            if (visited[nx][ny][dir] > now.turn) {
                visited[nx][ny][dir] = now.turn;
                queue[rear++] = (Node){nx, ny, dir, now.turn};
            }
        }

        // 尝试转向
        for (int d = 0; d < 4; d++) {
            if (d == dir)
                continue; //一致的就跳过
            nx = now.x + dx[d];
            ny = now.y + dy[d];

            if (nx >= 0 && nx < h && ny >= 0 && ny < w && map[nx][ny] != '*') {
                if (visited[nx][ny][d] > now.turn + 1) {
                    visited[nx][ny][d] = now.turn + 1;
                    queue[rear++] = (Node){nx, ny, d, now.turn + 1};
                }
            }
        }
    }
其中我们先要确认初次的方向令他为dir
点击查看代码
// 向当前方向继续移动
        int nx = now.x + dx[dir];
        int ny = now.y + dy[dir];
在后续的转向操作中只要确认是否与他一致就确定整个路径中的turn的次数‘ 注意这里的第二次判断因为是进行的下一步` if (visited[nx][ny][d] > now.turn + 1) { visited[nx][ny][d] = now.turn + 1;` 所以要在原有基础上加一
点击查看代码
// 尝试转向
        for (int d = 0; d < 4; d++) {
            if (d == dir)
                continue;
            nx = now.x + dx[d];
            ny = now.y + dy[d];
最后在主函数中进行的,因为两只奶牛都用C来表示所以不分先后但是用数组c[2]来区分不是我的思路但是比我的更简便
点击查看代码
int cx[2], cy[2], count = 0;
    for (int i = 0; i < h; i++)
        for (int j = 0; j < w; j++)
            if (map[i][j] == 'C'){
                cx[count] = i;
                cy[count] = j;
                count++;
            }
    int ans = BFS(cx[0], cy[0], cx[1], cy[1]);

完整代码如下

点击查看代码
#include <stdio.h>

#define MAXN 105

typedef struct{
    int x,y;
    int dir;
    int turn;
}Node;

int w,h;
char map[MAXN][MAXN];
int visited[MAXN][MAXN][4];

int dx[] = {1,-1,0,0}; //通信的方向是上下左右
int dy[] = {0,0,1,-1};

int BFS(int sx, int sy, int ex, int ey){
    Node queue [MAXN * MAXN * 4];
    int front = 0, rear = 0;
    // 初始化 visited 数组
    for (int i = 0; i < h; i++)
        for (int j = 0; j < w; j++)
            for (int d = 0; d < 4; d++)
                visited[i][j][d] = 1e9;

    // 从起点向四个方向尝试出发
    for (int d = 0; d < 4; d++) {
        visited[sx][sy][d] = 0;
        queue[rear++] = (Node){sx, sy, d, 0};
    }
    while (front < rear) {
        Node now = queue[front++];
        int dir = now.dir;

        // 向当前方向继续移动
        int nx = now.x + dx[dir];
        int ny = now.y + dy[dir];

        if (nx >= 0 && nx < h && ny >= 0 && ny < w && map[nx][ny] != '*') {
            if (visited[nx][ny][dir] > now.turn) {
                visited[nx][ny][dir] = now.turn;
                queue[rear++] = (Node){nx, ny, dir, now.turn};
            }
        }

        // 尝试转向
        for (int d = 0; d < 4; d++) {
            if (d == dir)
                continue;
            nx = now.x + dx[d];
            ny = now.y + dy[d];

            if (nx >= 0 && nx < h && ny >= 0 && ny < w && map[nx][ny] != '*') {
                if (visited[nx][ny][d] > now.turn + 1) {
                    visited[nx][ny][d] = now.turn + 1;
                    queue[rear++] = (Node){nx, ny, d, now.turn + 1};
                }
            }
        }
    }

    // 取四个方向的最小值
    int result = 1e9;
    for (int d = 0; d < 4; d++) {
        if (visited[ex][ey][d] < result)
            result = visited[ex][ey][d];
    }
    return result;
}
int main(){
    scanf("%d%d",&w, &h);
    for (int i = 0; i < h; i++) {
        scanf("%s", map[i]);
    }
    //因为是两只C奶牛之间的通信,用数组记录两只先后出现的奶牛
    int cx[2], cy[2], count = 0;
    for (int i = 0; i < h; i++)
        for (int j = 0; j < w; j++)
            if (map[i][j] == 'C'){
                cx[count] = i;
                cy[count] = j;
                count++;
            }
    int ans = BFS(cx[0], cy[0], cx[1], cy[1]);
    printf("%d\n", ans);
    return 0;
}


posted @ 2025-05-21 20:41  sirro1uta  阅读(13)  评论(0)    收藏  举报