news 2026/5/15 0:16:14

UVa 225 Golygons

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
UVa 225 Golygons

题目分析

Golygon\texttt{Golygon}Golygon是一种特殊的网格路径,它从原点(0,0)(0,0)(0,0)出发,第一步走111个单位,第二步走222个单位,依此类推,第nnn步走nnn个单位,且每一步必须向左转或向右转90∘90^\circ90(不能直行或掉头),最终回到原点(0,0)(0,0)(0,0)。路径可以自交。

本题在标准Golygon\texttt{Golygon}Golygon问题的基础上增加了障碍物:某些网格点被封锁,路径不能经过这些点。需要找出所有满足条件的Golygon\texttt{Golygon}Golygon,并按照字典序输出路径方向序列(n\texttt{n}n表示北,s\texttt{s}s表示南,e\texttt{e}e表示东,w\texttt{w}w表示西)。

输入包含多个测试用例,每个用例给出最大步长n≤20n \leq 20n20和障碍物坐标(不超过505050个)。

解题思路

状态表示与搜索

使用深度优先搜索 (DFS\texttt{DFS}DFS) 枚举所有可能的路径。每步需要记录:

  • 当前位置(x,y)(x, y)(x,y)
  • 当前行进方向(上一步的方向)
  • 当前步长(已经走了几步)

方向用数字表示:000代表东 (e\texttt{e}e),111代表北 (n\texttt{n}n),222代表南 (s\texttt{s}s),333代表西 (w\texttt{w}w)。

转向规则

由于不能直行或掉头,每一步只能左转或右转90∘90^\circ90。用turn数组预计算两种转向结果:

当前方向左转结果右转结果
000(东)111(北)222(南)
111(北)333(西)000(东)
222(南)000(东)333(西)
333(西)222(南)111(北)

剪枝策略

由于n≤20n \leq 20n20,直接搜索复杂度较高,需要剪枝:

  1. 可行性剪枝:如果当前点到原点的曼哈顿距离大于剩余步数所能走的最大距离,则此路径不可能回到原点。剩余步数kkknnn的最大步长和为:

    (k+n)×(n−k+1)2\frac{(k + n) \times (n - k + 1)}{2}2(k+n)×(nk+1)

    如果∣x∣+∣y∣>这个值|x| + |y| > \text{这个值}x+y>这个值,直接剪枝。

  2. 障碍物检查:移动过程中,如果经过的线段上有障碍物,则此方向无效。

  3. 重复点标记:用grid数组记录已经访问过的点,避免重复经过(路径可以自交,但重复访问点会导致无限循环,需要阻止)。

坐标偏移

障碍物坐标范围未知,但根据步长限制,最大可能坐标范围为:

  • 最大步数n=20n = 20n=20,位移最大值为1+2+⋯+20=2101 + 2 + \cdots + 20 = 2101+2++20=210
  • 考虑正负方向,坐标范围约为[−210,210][-210, 210][210,210]

使用base = 250将坐标偏移到非负索引,方便数组访问。

输出顺序

要求按字典序输出方向序列。由于方向编码e<n<s<w\texttt{e} < \texttt{n} < \texttt{s} < \texttt{w}e<n<s<w,在搜索时按此顺序尝试即可保证字典序输出。

算法复杂度

最坏情况下分支数为2n2^n2n,但剪枝大大减少了搜索空间。对于n≤20n \leq 20n20,实际运行时间可以接受。

代码实现

// Golygons// UVa ID: 225// Verdict: Accepted// Submission Date: 2016-05-24// UVa Run Time: 0.390s//// 版权所有(C)2016,邱秋。metaphysis # yeah dot net#include<bits/stdc++.h>usingnamespacestd;// 点的结构体structpoint{intx,y;};vector<point>blocks;// 存储障碍物坐标vector<int>golygons(30);// 记录每一步的方向string headingText="ensw";// 方向字符映射,顺序:东、北、南、西// turn[当前方向][0]为左转,turn[当前方向][1]为右转intturn[4][2]={{1,2},{0,3},{0,3},{1,2}};// 四个方向对应的坐标变化:东、北、南、西intdirection[4][2]={{1,0},{0,1},{0,-1},{-1,0}};intgrid[500][500]={0},base=250;// 访问标记数组,base为坐标偏移量intnumberOfGolygons,longestEdge;// 解的数量和最大步长// 检查从 start 到 end 的线段上是否有障碍物boolblocked(point start,point end){for(inti=0;i<blocks.size();i++){// 障碍物在水平线段上(x相同)if(blocks[i].x==start.x&&blocks[i].x==end.x)if(blocks[i].y>=min(start.y,end.y)&&blocks[i].y<=max(start.y,end.y))returntrue;// 障碍物在垂直线段上(y相同)if(blocks[i].y==start.y&&blocks[i].y==end.y)if(blocks[i].x>=min(start.x,end.x)&&blocks[i].x<=max(start.x,end.x))returntrue;}returnfalse;}// 深度优先搜索// traveler: 当前位置, heading: 当前方向, edge: 当前步长(已走步数)voiddfs(point traveler,intheading,intedge){// 剪枝:如果当前位置到原点的曼哈顿距离大于剩余步数能走的最大距离,则不可能返回if((abs(traveler.x)+abs(traveler.y))>((edge+longestEdge)*(longestEdge-edge+1)/2))return;// heading < 0 表示第一步,需要尝试所有四个方向if(heading<0){for(inti=0;i<4;i++){point newTraveler;newTraveler.x=traveler.x+direction[i][0];newTraveler.y=traveler.y+direction[i][1];// 检查第一步是否撞到障碍物if(blocked(traveler,newTraveler))continue;// 标记访问,记录方向,继续搜索grid[newTraveler.x+base][newTraveler.y+base]=1;golygons[edge]=i;dfs(newTraveler,i,1);grid[newTraveler.x+base][newTraveler.y+base]=0;}}// 已走到最后一步,检查是否回到原点elseif(edge==longestEdge){if(traveler.x==0&&traveler.y==0){// 输出方向序列for(inti=0;i<edge;i++)cout<<headingText[golygons[i]];cout<<endl;numberOfGolygons++;}}else{// 左转的情况intturn1=turn[heading][0];point end1;end1.x=traveler.x+direction[turn1][0]*(edge+1);end1.y=traveler.y+direction[turn1][1]*(edge+1);// 检查是否撞到障碍物且终点未被访问过if(blocked(traveler,end1)==false&&grid[end1.x+base][end1.y+base]==0){grid[end1.x+base][end1.y+base]=1;golygons[edge]=turn1;dfs(end1,turn1,edge+1);grid[end1.x+base][end1.y+base]=0;}// 右转的情况intturn2=turn[heading][1];point end2;end2.x=traveler.x+direction[turn2][0]*(edge+1);end2.y=traveler.y+direction[turn2][1]*(edge+1);if(blocked(traveler,end2)==false&&grid[end2.x+base][end2.y+base]==0){grid[end2.x+base][end2.y+base]=1;golygons[edge]=turn2;dfs(end2,turn2,edge+1);grid[end2.x+base][end2.y+base]=0;}}}intmain(intargc,char*argv[]){intcases,numberOfBlocks,x,y;cin>>cases;while(cases--){cin>>longestEdge;intnumberOfBlocks,x,y;blocks.clear();cin>>numberOfBlocks;for(inti=1;i<=numberOfBlocks;i++){cin>>x>>y;blocks.push_back((point){x,y});}point traveler;traveler.x=0;traveler.y=0;numberOfGolygons=0;dfs(traveler,-1,0);cout<<"Found "<<numberOfGolygons<<" golygon(s)."<<endl;cout<<endl;}return0;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/15 0:11:03

如何快速整理桌面窗口:3个高效管理秘诀让工作区更清爽

如何快速整理桌面窗口&#xff1a;3个高效管理秘诀让工作区更清爽 【免费下载链接】traymond A simple Windows app for minimizing windows to tray icons 项目地址: https://gitcode.com/gh_mirrors/tr/traymond 你是否经常因为任务栏拥挤而找不到关键窗口&#xff1f…

作者头像 李华
网站建设 2026/5/15 0:01:08

电脑小白也能行,十分钟搞定 OpenClaw 一键部署

开始前的小准备&#xff1a;让安装过程更顺畅 【点击下载最新安装包】 很多办公族朋友听到“部署”、“开源工具”或者“命令行”这些词&#xff0c;第一反应往往是头大&#xff0c;觉得这是程序员专属的领域&#xff0c;自己肯定搞不定。其实&#xff0c;像 OpenClaw 这样的数…

作者头像 李华
网站建设 2026/5/14 23:58:05

Linux查看进程命令

在 Linux 系统中&#xff0c;查看进程的命令非常丰富&#xff0c;针对不同的排查需求&#xff08;如查看实时负载、精准定位某个程序、分析进程关系等&#xff09;&#xff0c;有不同的实用工具。以下是按使用场景分类的常用进程查看命令&#xff1a; &#x1f4f8; 基础静态快…

作者头像 李华