每周一算法:旋转游戏

题目链接

旋转游戏(The Rotation Game)

题目描述

如图 1 1 1 所示,有一个 #形的棋盘,上面有 1 , 2 , 3 1,2,3 1,2,3 三种数字各 8 8 8 个。给定 8 8 8 种操作,分别为图中的 A ∼ H \text{A}\sim \text{H} A∼H。这些操作会按照图中字母与箭头所指明的方向,把一条长度为 8 8 8 的序列循环移动 1 1 1 个单位。例如下图最左边的 # 形棋盘执行操作 A \text{A} A 时,会变为图中间的 # 形棋盘,再执行操作 C \text{C} C 后会变为图中最右边的 # 形棋盘。

图 1 \text{图 1} 图 1

现给定一个初始状态,请使用最少的操作次数,使 # 形棋盘最中间的 8 8 8 个格子里的数字相同。

输入格式

输入包括不超过 30 30 30 组测试数据。

每个测试数据只包括一行,包含 24 24 24 个整数,每相邻两个整数之间用 1 1 1 个空格隔开,表示这个 # 形棋盘的初始状态。(这些整数的排列顺序是从上至下,同一行的从左至右。

例如 1 1 1 1 3 2 3 2 3 1 3 2 2 3 1 2 2 2 3 1 2 1 3 3 \text{1 1 1 1 3 2 3 2 3 1 3 2 2 3 1 2 2 2 3 1 2 1 3 3} 1 1 1 1 3 2 3 2 3 1 3 2 2 3 1 2 2 2 3 1 2 1 3 3 表示图 1 1 1 最左边的状态。)每两组测试数据之间没有换行符。

输入文件以一行 0 0 0 结束。

输出格式

对于每组测试数据,输出两行。第一行用字符 A ∼ H \text{A}\sim \text{H} A∼H 输出操作的方法,每两个操作字符之间没有空格分开 ,如果不需要任何步数,输出 No moves needed

第二行输出最终状态中最中间的 8 8 8 个格子里的数字。

如果有多组解,输出操作次数最少的一组解;如果仍有多组解,输出字典序最小的一组。任意相邻两组测试数据的输出之间不需输出换行符。

输入样例

复制代码
1 1 1 1 3 2 3 2 3 1 3 2 2 3 1 2 2 2 3 1 2 1 3 3
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3
0

输出样例

复制代码
AC
2
DDHH
2

算法思想

根据题目描述,每一步操作有 A ∼ H \text{A}\sim \text{H} A∼H一共 8 8 8种选择,状态空间随着深度增加呈指数级别增长,但是答案序列不会太长,为了避免搜索超时,可以使用迭代加深A*( IDA* \text{IDA*} IDA*)实现。

估价函数

首先来设计估价函数。在每个状态下,如果中间 8 8 8个格子里出现次数最多的是 x x x,一共出现了 m m m次,那么其余数字全变成 x x x,至少需要 8 − m 8-m 8−m次操作。可以用这个作为预估步数。

算法实现

再来确定搜索的基本框架,对于每个状态:

  • 如果当前步数 + 未来估计步数>深度限制,则搜索结束,返回无解
  • 如果到达最终状态,则搜索结束,返回有解。
  • 对于当前状态,枚举要进行的操作 A ∼ H \text{A}\sim \text{H} A∼H
    • 为了满足题目中对字典序的要求,先进行字典序小的操作
    • 然后沿着该分支深入即可。

剪枝

如果当前操作跟上一次的操作相反,那么相当于没有进行任何操作,所以在枚举时,不能执行上次操作的逆操作,避免来回搜索。

例如,执行了 A \text{A} A操作,又执行了 F \text{F} F操作,棋盘没有变化。

所以需要记录一下上一步的操作last,在枚举时进行剪枝。

代码实现

cpp 复制代码
/*
       A     B
       0     1
       2     3
H4  5  6  7  8  9  10 C
       11    12
G13 14 15 16 17 18 19 D
       20    21
       22    23
       F     E
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 25;
int q[N], path[100]; //path记录操作
int op[8][7] = { //记录每种操作对应的格子编号
  {0, 2, 6, 11, 15, 20, 22}, //A
  {1, 3, 8, 12, 17, 21, 23}, //B
  {10, 9, 8, 7, 6, 5, 4}, //C
  {19, 18, 17, 16, 15, 14, 13}, //D
  {23, 21, 17, 12, 8, 3, 1}, //E
  {22, 20, 15, 11, 6, 2, 0}, //F
  {13, 14, 15, 16, 17, 18, 19}, //G
  {4, 5, 6, 7, 8, 9, 10} //H
};
int center[8] = {6, 7, 8, 11, 12, 15, 16, 17}; //中间8个格子
int oppsite[8] = {5, 4, 7, 6, 1, 0, 3, 2}; //每个操作的逆操作
//估计函数,对于出现次数最多的x,返回m个数字x不同
int h() 
{
    int s[4] = {0};
    for(int i = 0; i < 8; i ++) s[q[center[i]]]++;
    int m = 0;
    for(int i = 1; i <= 3; i ++) m = max(m, s[i]);
    return 8 - m;
}
bool check() //检查是否到达最终状态
{
    for(int i = 1; i < 8; i ++)
        if(q[center[i]] != q[center[0]]) return false;
    return true;
}
void work(int x) //模拟操作x
{
    int t = q[op[x][0]];
    for(int i = 0; i < 6; i ++) q[op[x][i]] = q[op[x][i + 1]];
    q[op[x][6]] = t;
}
bool dfs(int k, int depth, int last) 
{
    if(k + h() > depth) return false;
    if(check()) return true;
    for(int i = 0; i < 8; i ++) //枚举操作,从A~H,满足字典序
    {
        if(oppsite[i] == last) continue; //是上一次的逆操作,剪枝
        work(i); //进行i操作
        path[k] = i; //记录第k步的操作
        if(dfs(k + 1, depth, i)) return true; 
        work(oppsite[i]); //恢复现场
    }
    return false;
}
int main()
{
    while(scanf("%d", &q[0]), q[0])
    {
        for(int i = 1; i < 24; i ++) cin >> q[i];
        int depth = 0;
        while(!dfs(0, depth, -1)) depth ++;
        if(depth == 0) printf("No moves needed");
        for(int i = 0; i < depth; i ++) printf("%c", 'A' + path[i]);
        printf("\n%d\n", q[6]); //输出中间8格的数字
    }
    return 0;
}
相关推荐
@小码农1 分钟前
202512 电子学会 Scratch图形化编程等级考试三级真题(附答案)
服务器·开发语言·数据结构·数据库·算法
橘颂TA19 分钟前
【剑斩OFFER】算法的暴力美学——重排链表
算法·结构与算法
zl_vslam29 分钟前
SLAM中的非线性优-3D图优化之相对位姿Between Factor位姿图优化(十三)
人工智能·算法·计算机视觉·3d
千里马-horse31 分钟前
Rect Native bridging 源码分析--AString.h
c++·ts·rn·jsi
Timmylyx051832 分钟前
CF 新年赛 Goodbye 2025 题解
算法·codeforces·比赛日记
闻缺陷则喜何志丹33 分钟前
【二分查找】P10091 [ROIR 2022 Day 2] 分数排序|普及+
c++·算法·二分查找
only-qi1 小时前
leetcode2. 两数相加
算法·leetcode
鲨莎分不晴1 小时前
拯救暗淡图像:深度解析直方图均衡化(原理、公式与计算)
人工智能·算法·机器学习
DuHz1 小时前
242-267 GHz双基地超外差雷达系统:面向精密太赫兹传感与成像的65nm CMOS实现——论文阅读
论文阅读·物联网·算法·信息与通信·毫米波雷达
AI科技星2 小时前
时空的固有脉动:波动方程 ∇²L = (1/c²) ∂²L/∂t² 的第一性原理推导、诠释与验证
数据结构·人工智能·算法·机器学习·重构