news 2026/5/25 19:49:02

UVa 1533 Moving Pegs

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
UVa 1533 Moving Pegs

题目描述

Venture MFG\texttt{Venture MFG}Venture MFG公司设计了一个带有151515个洞的游戏板,初始状态除一个指定的洞为空外,其余洞均插有木钉。游戏规则是:木钉可以沿直线跳过一个或多个连续的木钉,跳到最近的空洞。被跳过的木钉将被移除。

目标:找到最短的移动序列,使得最后只剩下一个木钉,并且该木钉位于最初为空的那个洞中。如果不存在这样的序列,输出IMPOSSIBLE\texttt{IMPOSSIBLE}IMPOSSIBLE

输入格式

第一行包含测试用例数TTT,每个测试用例是一个整数,表示初始空洞的编号(111151515)。

输出格式

对于每个测试用例,第一行输出最短序列的跳跃次数,第二行输出跳跃序列。每个跳跃由两个用空格分隔的整数表示:移动的木钉所在洞的编号和目标空洞的编号。

样例输入

1 5

样例输出

10 12 5 3 8 15 12 6 13 7 9 1 7 10 8 7 9 11 14 14 5

重要补充说明

题目描述不完整:根据实际评测系统的要求,本题需要输出字典序最小的最短序列。虽然原始题目描述中没有明确说明这一要求,但评测系统实际上按照字典序进行比对。

字典序定义

对于两个操作序列A=[(a1,b1),(a2,b2),...,(an,bn)]A = [(a_1, b_1), (a_2, b_2), ..., (a_n, b_n)]A=[(a1,b1),(a2,b2),...,(an,bn)]B=[(c1,d1),(c2,d2),...,(cn,dn)]B = [(c_1, d_1), (c_2, d_2), ..., (c_n, d_n)]B=[(c1,d1),(c2,d2),...,(cn,dn)]

  1. 比较第一个操作的起始洞编号:如果a1<c1a_1 < c_1a1<c1,则A<BA < BA<B
  2. 如果a1=c1a_1 = c_1a1=c1,则比较第一个操作的目标洞编号:如果b1<d1b_1 < d_1b1<d1,则A<BA < BA<B
  3. 如果第一个操作完全相同,则比较第二个操作,依此类推

解题思路分析

方法一:实时生成解(BFS\texttt{BFS}BFS+ 位运算 + 字典序优化)

这种方法通过广度优先搜索(BFS\texttt{BFS}BFS)探索所有可能的状态,并通过排序保证找到字典序最小的解。

1. 状态表示

使用151515位二进制数表示棋盘状态,第iii位(1≤i≤151 \le i \le 151i15)表示洞iii是否有木钉:111表示有木钉,000表示空。

  • 初始状态:除了指定的空洞外,其他所有洞都有木钉
  • 目标状态:只有指定的空洞有木钉
2. 跳跃规则预计算

棋盘上合法的跳跃必须满足:

  1. 起始洞必须有木钉
  2. 目标洞必须为空
  3. 起始洞和目标洞必须在同一条直线上
  4. 必须跳过至少一个木钉
  5. 目标洞必须是跳跃方向上的第一个空洞

我们为每条直线生成所有可能的跳跃规则,并进行字典序排序

// 按字典序排序:先按from排序,from相同按to排序sort(jumps.begin(),jumps.end(),[](constjump&a,constjump&b){if(a.from!=b.from)returna.from<b.from;returna.to<b.to;});
3.BFS\texttt{BFS}BFS搜索策略

使用BFS\texttt{BFS}BFS从初始状态开始搜索,确保按层扩展,从而找到最短路径。由于跳跃规则已按字典序排序,BFS\texttt{BFS}BFS会优先尝试字典序小的操作。

关键算法步骤

  1. 初始化队列和访问集合
  2. 从队列中取出状态
  3. 按排序后的顺序尝试所有合法跳跃
  4. 生成新状态并加入队列
  5. 找到目标状态时返回路径
4. 算法正确性证明
  1. 最短性保证BFS\texttt{BFS}BFS按层扩展,第一次到达目标状态时路径最短
  2. 字典序最小保证:对于同一层的状态,按字典序顺序尝试跳跃,确保找到的第一个解字典序最小
  3. 完备性:遍历所有可达状态,确保不会遗漏解
5. 算法复杂度分析
  • 状态总数:215=327682^{15} = 32768215=32768种可能状态
  • 每个状态的合法跳跃数:约363636
  • 时间复杂度:O(32768×36)≈1.2×106O(32768 \times 36) \approx 1.2 \times 10^6O(32768×36)1.2×106,在可接受范围内
  • 空间复杂度:O(32768)O(32768)O(32768)存储访问状态

方法二:硬编码方案

由于已知所有测试用例的正确答案,可以直接硬编码解决方案。这种方法的优势是100%100\%100%可靠且效率极高。

// Moving Pegs// UVa ID: 1533// Verdict: Accepted// Submission Date: 2025-12-17// UVa Run Time: 0.000s//// 版权所有(C)2025,邱秋。metaphysis # yeah dot net#include<iostream>usingnamespacestd;intmain(){intcnt[15]={9,9,9,9,10,9,9,10,10,9,9,9,9,9,9};string moves[15]={"4 1 6 4 11 2 14 11 15 6 1 10 10 7 11 4 4 1","7 2 1 4 10 1 14 2 1 7 11 14 15 13 13 4 7 2","10 3 1 6 7 1 12 3 1 10 14 12 11 13 13 6 15 3","1 4 6 1 13 6 10 3 11 13 3 12 15 11 11 2 1 4","12 5 3 8 15 12 6 13 7 9 1 7 10 8 7 9 11 14 14 5","1 6 4 1 13 4 7 2 15 13 2 14 11 15 15 3 1 6","1 7 6 1 11 4 9 7 14 11 11 2 15 6 6 4 1 7","3 8 12 5 15 3 13 6 1 10 2 9 11 2 14 5 2 9 10 8","2 9 11 2 12 5 3 8 13 4 1 7 14 5 15 3 3 8 7 9","1 10 4 1 11 4 4 6 12 5 10 8 15 12 12 3 1 10","4 11 1 4 10 1 14 2 1 7 11 14 15 13 13 4 4 11","14 12 2 14 7 2 1 4 15 13 3 15 11 14 4 13 15 12","4 13 1 4 11 2 13 11 15 13 2 14 3 15 15 12 11 13","11 14 2 11 3 12 10 3 1 6 11 13 15 12 6 13 12 14","6 15 1 6 7 1 12 3 1 10 15 12 11 13 13 6 6 15"};intT;cin>>T;while(T--){intemptyHole;cin>>emptyHole;cout<<cnt[emptyHole-1]<<'\n';cout<<moves[emptyHole-1]<<'\n';}return0;}

代码实现(实时生成解)

以下是提交通过的实时生成解代码:

// Moving Pegs// UVa ID: 1533// Verdict: Accepted// Submission Date: 2025-12-17// UVa Run Time: 0.050s//// 版权所有(C)2025,邱秋。metaphysis # yeah dot net#include<bits/stdc++.h>usingnamespacestd;// 棋盘布局(洞编号):// (1)// (2) (3)// (4) (5) (6)// (7) (8) (9) (10)//(11)(12)(13)(14)(15)// 所有可能的直线(至少3个洞在一条直线上)constvector<vector<int>>lines={{1,2,4,7,11},{3,5,8,12},{6,9,13},{4,5,6},{7,8,9,10},{11,12,13,14,15},{1,3,6,10,15},{2,5,9,14},{4,8,13},};structjump{intmask,bit1,bit2,from,to;};vector<jump>jumps;voidgetPossibleMoves(){for(autoline:lines){for(inti=0;i<line.size();i++)for(intj=i+2;j<line.size();j++){jump jmp=jump{0,0,0,line[j],line[i]};for(intk=i;k<=j;k++)jmp.mask|=1<<(line[k]-1);for(intk=i+1;k<=j;k++)jmp.bit1|=1<<(line[k]-1);jmp.bit2=1<<(line[i]-1);jumps.push_back(jmp);}for(inti=line.size()-1;i>=0;i--)for(intj=i-2;j>=0;j--){jump jmp=jump{0,0,0,line[j],line[i]};for(intk=i;k>=j;k--)jmp.mask|=1<<(line[k]-1);for(intk=i-1;k>=j;k--)jmp.bit1|=1<<(line[k]-1);jmp.bit2=1<<(line[i]-1);jumps.push_back(jmp);}}// 按字典序排序:先按from排序,from相同按to排序sort(jumps.begin(),jumps.end(),[](constjump&a,constjump&b){if(a.from!=b.from)returna.from<b.from;returna.to<b.to;});}structstate{intbits;vector<pair<int,int>>moves;};vector<pair<int,int>>solve(intemptyHole){intend=1<<(emptyHole-1);unordered_set<int>visited;state start;start.bits=~(1<<(emptyHole-1))&((1<<15)-1);queue<state>q;q.push(start);visited.insert(start.bits);while(!q.empty()){state s=q.front();q.pop();if(s.bits==end)returns.moves;for(autoj:jumps){if((s.bits&j.mask)==j.bit1){state next={s.bits&(~j.mask)|j.bit2,s.moves};next.moves.push_back(make_pair(j.from,j.to));if(!visited.count(next.bits)){visited.insert(next.bits);q.push(next);}}}}return{};}intmain(){getPossibleMoves();intT;cin>>T;while(T--){intemptyHole;cin>>emptyHole;automoves=solve(emptyHole);if(moves.empty())cout<<"IMPOSSIBLE\n";else{cout<<moves.size()<<'\n';for(size_t i=0;i<moves.size();i++){if(i)cout<<' ';cout<<moves[i].first<<' '<<moves[i].second;}cout<<'\n';}}return0;}

关键代码解析

1. 跳跃规则数据结构

structjump{intmask,bit1,bit2,from,to;};
  • mask:涉及的所有洞的位掩码
  • bit1:需要检查的木钉位置的位掩码(起始洞和中间洞)
  • bit2:目标洞的位掩码
  • from:起始洞编号
  • to:目标洞编号

2. 跳跃有效性检查

if((s.bits&j.mask)==j.bit1)

这个条件检查:

  1. 起始洞有木钉(包含在bit1中)
  2. 所有中间洞都有木钉(包含在bit1中)
  3. 目标洞为空(不在bit1中但在mask中)

3. 状态转移

state next={s.bits&(~j.mask)|j.bit2,s.moves};
  • s.bits & (~j.mask):清除涉及的所有洞
  • | j.bit2:在目标洞放置木钉

两种方案的比较

特性实时生成解硬编码方案
通用性高,可扩展低,仅限于当前问题
通过率100%(正确实现时)100%
时间复杂度O(1.2×106)O(1.2 \times 10^6)O(1.2×106)O(1)O(1)O(1)
空间复杂度O(32768)O(32768)O(32768)O(270)O(270)O(270)
实现难度中等简单
学习价值

经验总结

  1. 仔细阅读题目要求:即使题目描述不完整,也要通过测试样例推断隐含要求
  2. 字典序处理:当输出序列不唯一时,需要考虑字典序要求
  3. 状态压缩技巧:使用位运算可以有效表示和操作棋盘状态
  4. 预计算优化:提前计算所有可能的跳跃规则可以显著提高效率
  5. BFS的正确性:通过排序跳跃规则,可以保证BFS\texttt{BFS}BFS找到字典序最小的解

扩展思考

  1. 如果棋盘布局变化(如更多洞),算法如何扩展?
  2. 如何证明这个BFS\texttt{BFS}BFS算法一定能找到最短路径?
  3. 如果要求输出所有最短序列,算法应如何修改?
  4. 是否存在启发式搜索(如A*\texttt{A*}A*)的优化空间?

本题展示了状态空间搜索、位运算优化和字典序处理的综合应用,是一个优秀的算法练习题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/26 4:37:48

屏幕录制新选择:vokoscreenNG如何解决你的录制难题

屏幕录制新选择&#xff1a;vokoscreenNG如何解决你的录制难题 【免费下载链接】vokoscreenNG vokoscreenNG is a powerful screencast creator in many languages to record the screen, an area or a window (Linux only). Recording of audio from multiple sources is supp…

作者头像 李华
网站建设 2026/5/25 23:47:03

一键部署EmotiVoice Docker镜像的操作步骤

一键部署 EmotiVoice Docker 镜像的操作步骤 在语音交互日益成为主流人机接口的今天&#xff0c;用户对“机器说话”的期待早已不再满足于简单的信息播报。他们希望听到更自然、更有情绪、甚至带有“人格感”的声音——无论是陪伴型AI助手的一句温柔问候&#xff0c;还是游戏角…

作者头像 李华
网站建设 2026/5/26 4:39:06

语音合成艺术化探索:用EmotiVoice创作声音装置

语音合成艺术化探索&#xff1a;用EmotiVoice创作声音装置 在一场关于“记忆”的互动展览中&#xff0c;观众走近一座老式留声机雕塑。它缓缓启动&#xff0c;播放一段低语&#xff1a;“那年夏天&#xff0c;我们还在河边捉蜻蜓。”声音温柔而略带颤抖——是怀念&#xff1f;…

作者头像 李华
网站建设 2026/5/26 4:39:20

7、Web应用程序漏洞检测与自动化扫描工具指南

Web应用程序漏洞检测与自动化扫描工具指南 在网络安全领域,对Web应用程序进行漏洞检测是至关重要的。本文将介绍文件包含漏洞检测、POODLE漏洞识别以及几种常用的自动化扫描工具的使用方法。 文件包含漏洞检测 文件包含漏洞通常在开发人员使用可被用户修改的请求参数来动态…

作者头像 李华
网站建设 2026/5/26 5:39:25

9、Web安全攻击技术实战指南

Web安全攻击技术实战指南 1. THC-Hydra暴力破解密码 1.1 THC-Hydra简介 THC-Hydra(简称Hydra)是一款网络登录破解工具,属于在线破解器,可通过暴力破解网络服务来查找登录密码。暴力攻击是尝试所有可能的字符组合以猜测正确密码,这种方式虽耗时,但一定能找到答案。 1.…

作者头像 李华
网站建设 2026/5/25 22:09:36

12、高级渗透测试技术与中间人攻击全解析

高级渗透测试技术与中间人攻击全解析 在渗透测试中,获取服务器的 shell 只是第一步,后续还需要进行权限提升并获取有助于测试的信息。以下将详细介绍一些高级渗透技术和中间人攻击方法。 利用 John the Ripper 通过字典破解密码哈希 在渗透测试中,从数据库提取密码哈希是常…

作者头像 李华