题解:洛谷 P9945 Clockwise Fence
【题目来源】
洛谷:P9945 [USACO21FEB] Clockwise Fence B - 洛谷
【题目描述】
围绕 Farmer John 最大的草地的栅栏已经损坏了,如今他终于决定要换一个新的栅栏。
不幸的是,当 Farmer John 在铺设新栅栏时,一只巨大的蜜蜂突然出现,在他的草地上追着他跑,导致最后栅栏被沿着一条相当不规则的路径铺设。栅栏可以用一个字符串表示,每个字符为 N(north,北)、E(east,东)、S(south,南)、W(west,西)之一。每个字符表示一米长的一段栅栏。举例来说,如果字符串为 NESW,这表示栅栏从起点开始向北延伸 \(1\) 米,然后向东延伸 \(1\) 米,然后向南延伸 \(1\) 米,然后向西延伸 \(1\) 米,回到栅栏的起点。
栅栏的结束位置与开始位置相同,而这是栅栏的路径上唯一会被到达多次的位置(从而起始位置是唯一会被再次到达的位置,在栅栏结束之时)。结果,栅栏确实围起了一个草地上连通的区域,尽管这个区域可能形状十分奇特。
Farmer John 想要知道他铺设栅栏的路径是顺时针(当按字符串表示的顺序沿着栅栏的路径行走时被围起的区域位于右侧)还是逆时针(被围起的区域位于左侧)。
【输入】
输入的第一行包含一个整数 \(N\)(\(1\le N\le 20\))。以下 \(N\) 行每行包含一个长度不小于 \(4\) 且不超过 \(100\) 的字符串,表示一个栅栏的路径。
【输出】
对 \(N\) 条输入的栅栏路径的每一条,输出一行,为 CW(clockwise,顺时针)或 CCW(counterclockwise,逆时针)。
【输入样例】
2
NESW
WSSSEENWNEESSENNNNWWWS
【输出样例】
CW
CCW
【核心思想】
-
问题分析:给定一个由
N、E、S、W组成的闭合路径字符串(起点终点重合且路径不自交),需要判断沿该路径行走时,被围成的区域位于右侧(顺时针,CW)还是左侧(逆时针,CCW)。本题无需计算面积或复杂几何,只需利用方向变化的转角累加来判断整体旋转方向。 -
算法选择:
- 转角累加法:遍历路径中的每一对相邻方向,计算从当前方向转到下一个方向所需的有符号旋转角(左转为 +90°,右转为 -90°)。将所有转角累加,若总和为 +360° 则为逆时针(CCW),若为 -360° 则为顺时针(CW)。这是基于闭合路径的总旋转角必须为 ±360° 的拓扑性质。
- 方向映射:用数字 0~3 分别表示
E、W、S、N,并预先定义一个 4×4 的转角表d[x][y],存储从方向x转到方向y的角度变化(+90、-90 或 0)。
-
关键步骤:
- 读入:输入 \(N\)(测试用例数),对每个字符串 \(s\) 进行处理。
- 预处理字符串:在 \(s\) 末尾添加其首字符
s[0],以便处理最后一条边回到起点的转向(闭合路径的最后一段方向与第一段方向之间的转角也必须计入)。 - 遍历相邻方向对:对于 \(i\) 从 0 到 \(len(s)-2\),获取 \(s[i]\) 和 \(s[i+1]\) 对应的方向索引
x和y,从转角表d[x][y]中取出角度变化,累加到ans中。 - 判断并输出:若
ans == 360,输出"CW";若ans == -360,输出"CCW"。
-
时间/空间复杂度:
- 时间复杂度:\(O(N \cdot L)\),其中 \(L\) 为路径字符串长度(≤100),\(N \le 20\)。
- 空间复杂度:\(O(1)\),仅存储转角表和少量变量。
-
转角累加法的核心思想:
- 几何性质:对于一条不自交的闭合曲线,沿其行进方向,所有转角的代数和必须为 ±360°(即方向向量旋转的总角度)。这个总角度正好对应路径的环绕方向:逆时针为 +360°,顺时针为 -360°。
- 局部转向计算:路径由单位长度的水平和垂直段组成,因此每个转角只能是 ±90° 或 0°(直行)。只需比较相邻两步的方向即可确定转向。
- 闭合处理:在字符串末尾添加首字符,确保从最后一段方向回到第一段方向的转角也被计入,否则会遗漏 +90° 或 -90°。
- 适用场景:适用于判断简单正交多边形(grid-aligned polygon)的环绕方向,无需计算面积或叉积,仅通过方向序列即可判定。
【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, ans;
string s;
int d[4][4] = { // 定义二维数组,E为0,W为1,S为2,N为3,每个单元格的值为方向变化后的角度变化
{0,0,90,-90},
{0,0,-90,90},
{-90,90,0,0},
{90,-90,0,0}
};
int main()
{
cin >> n; // 输入n
for (int i=1; i<=n; i++) { // for循环遍历n次询问
cin >> s; // 输入字符串s
s = s + s[0]; // 因为最后要回到原点,所以在字符串最后加上起始字符
ans = 0; // 每轮询问角度统计都要归零
for (int i=0; i<s.length()-1; i++) { // for循环遍历s.length()-2个字符,因为要比较i与i+1
int x, y; // 定义x和y坐标
if (s[i]=='E') x=0; // 根据i的字符确定x坐标
if (s[i]=='W') x=1;
if (s[i]=='S') x=2;
if (s[i]=='N') x=3;
if (s[i+1]=='E') y=0; // 根据i+1的字符确定y坐标
if (s[i+1]=='W') y=1;
if (s[i+1]=='S') y=2;
if (s[i+1]=='N') y=3;
if (d[x][y]) ans += d[x][y]; // 当掉转角度不为0时,加上角度的变化。
}
if (ans==360) cout << "CW" << endl; // 当为360度时,说明是顺时针转回来
else if (ans==-360) cout << "CCW" << endl; // 当为-360度时,说明是逆时针转回来
}
return 0;
}
【运行结果】
2
NESW
CW
WSSSEENWNEESSENNNNWWWS
CCW
浙公网安备 33010602011771号