本文将以HDU1667为例,记录笔者学习IDA*的心路历程与一些浅见。本文将着重与上一篇IDDFS的笔记进行联系,比较IDA*与IDDFS的特征区别,同时修正一些对于迭代加深算法的见解。

首先上题:


旋转游戏使用一个#形状的板,可以容纳24块方块(见图1)。方块上标着符号1、2和3,每种方块都有8块。
    最初,方块是随机放置在棋盘上的。你的任务是移动方块,使放置在中心广场的八个方块有相同的标记。这里只有一种有效的移动类型,即旋转4行中的1行,每一行由7个块组成。即将一行中的6个block向头部移动1个block,将头部block移动到行尾。8种可能的走法用大写字母A到H标出。图1展示了两个连续的走法,从某个初始配置移动A和移动C。


Input:
    输入不超过30个测试用例。每个测试用例只有一行包含24个数字,这是初始配置中的块的符号。块的行是从上到下列出的。对于每一行,代码块从左到右列出。这些数字用空格隔开。例如,样本输入中的第一个测试用例对应于图1中的初始配置。案例之间没有空白行。在结束输入的最后一个测试用例之后,有一行包含一个' 0 '。
Output:
        对于每个测试用例,您必须输出两行。第一行包含到达最终配置所需的所有步骤。每一步都是一个字母,从A到H ,行内字母之间不能有空格。如果不需要移动,则输出“no moves needed”。在第二行中,您必须在这些移动之后输出中心方块中的方块符号。如果有几种可能的解决方案,你必须输出使用最少移动次数的那一种。如果仍然有多个可能的解决方案,您必须输出的解决方案中最小的字典顺序的字母的移动。case之间不需要输出空行。

Sample Input
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
Sample Output
AC
2
DDHH
2


对于IDA*而言,相较于IDDFS最大的地方是A*,即贪心算法的加入,具体来说就是增加了估价函数,作用与IDDFS中的迭代加深部分,用于限制IDDFS对DFS深度的规定数量,在很多题目中体现为不进行过小的深度计算,例如在HDU1560 DNA sequence中,小于输入中最小的DNA链的长度的枚举深度都是无效的等等。由于IDDFS的大量枚举时间都不由得地浪费在反复迭代加深的过程中,高效的估价函数能大大避免时间的浪费。

在上一篇IDDFS-埃及分数的笔记中,由于埃及分数题目的特殊性,我们无法加入合适的A*算法,而是只能在其中BFS部分加入划界函数规定它的有效宽度,使得题目至少在一个方向上是有限的,这至少给我们了一点原则性的启发:“避免无限、避免浪费。”便是IDDFS和IDA*的优化方向。

而在这道题中,解题的基本思路比较明确,题目已经给出了明确的值位移方式(旋转),无非就是用各位各自喜好的数据结构去储存和操作。笔者选择了字符串。

讲讲笔者自己的估价函数构造思路:在本题中,我们更关注的是中间8个方块的数值(有效区域)是否一样,而鉴于“旋转”这种操作,每旋转一次会有一个数字进入有效区域,一个数字离开有效区域,“一进一出”意味着一次旋转最多改变有效区域内两个方块的值,因此如果方块内不一样数字的个数除2>剩下的旋转次数(总深度减当前深度),则说明深度不够,不再枚举,跳过。

先放出估价函数的代码:

bool check(int delta){       //IDA*-剪枝 int sum = 0;string str;str = a[2]+c[3]+b[2]+b[3]+b[4]+d[3]+a[4]+a[3];for(int i=0;i<str.length()-1;i++){if(str[i]==str[i+1]) sum++;}return(sum/2>delta);
}

实际上估价函数(剪枝)的部分往往可以是最后思考的内容。解决了A*问题,让我们重新回到题目本身,首先明确IDDFS中DFS深度的数据意义,本题显然是旋转次数(往往深度和所求最优解统一数据意义),明确了这点,DFS部分基本完成了80%,接下来思考一下BFS广度搜索部分。

在DFS的内部,IDDFS往往需要一个BFS来完成搜索任务,本题与埃及分数不同,值位移的方向是有限的(8个旋转方向),即宽度是有限的,因此不需要划界函数来限制它(有的题目仍需要在BFS部分进一步优化)。有趣的是,宽度有限的BFS组成的IDFFS往往不需要队列的加入,因为它的数值变化方向是一定的,所以事实上DFS的回溯完成了队列queue的工作,并且比queue更好!

        首先给出值位移函数模块,由于用字符串储存数据,所以值位移函数是一个大switch,但操作并不复杂,同时按字典序排列,直接解决了题目要求的字典序问题:

void align(char i){  //动了谁,就以谁为标注对齐4个字符串 switch(i){case 'A':case 'B':case 'E':case 'F':{c[2] = a[2]; c[4] = b[2];d[2] = a[4];  d[4] = b[4];break;}case 'C':case 'D':case 'G':case 'H':a[2] = c[2];   b[2] = c[4];a[4] = d[2];  b[4] = d[4];break;}
}
string move(char i){    //1=A,2=B,3=C,4=D,5=G,6=H,7=E,8=F   9-X=X的对面 string str;switch(i){case 'A':{char ch = a[0];for(int i=0;i<6;i++){a[i] = a[i+1];}a[6] = ch;str = "A";break;}case 'B':{char ch = b[0];for(int i=0;i<6;i++){b[i] = b[i+1];}b[6] = ch;str = "B";break;}case 'C':{char ch = c[6];for(int i=6;i>0;i--){c[i] = c[i-1];}c[0] = ch;str = "C";break;}case 'D':{char ch = d[6];for(int i=6;i>0;i--){d[i] = d[i-1];}d[0] = ch;str = "D";break;}case 'G':{char ch = d[0];for(int i=0;i<6;i++){d[i] = d[i+1];}d[6] = ch;str = "G";break;}case 'H':{char ch = c[0];for(int i=0;i<6;i++){c[i] = c[i+1];}c[6] = ch;str = "H";break;}case 'E':{char ch = b[6];for(int i=6;i>0;i--){b[i] = b[i-1];}b[0] = ch;str = "E";break;}case 'F':{char ch = a[6];for(int i=6;i>0;i--){a[i] = a[i-1];}a[0] = ch;str = "F";break;}}align(i);return str;
}

对当前值进行位移,然后检测它,放出结果判断函数,一个简单粗暴的枚举判断:

bool finish(){return(a[2]==c[3] && c[3]==b[2] && b[2]==b[3] && b[3]==b[4] && b[4]==d[3] && d[3]==a[4] && a[4]==a[3] && a[3]==a[2]);
}

有了上面这些模块,IDA*部分的代码将变得简单,不过记得储存路径,本题用map<string,string>记录位移,回溯则删除末位:

bool IDA(int now, int deep){if(finish()){if(deep==0)   cout<<"no moves needed"<<endl;else    cout<<load<<endl;cout<<a[2]<<endl;return true;}if(check(deep-now))  return false;if(now == deep)      return false;for(char i='A';i<='H';i++){     //IDA-BFSgstring st;st = move(i);load += st;if(IDA(now+1,deep)) return true;move(m[i]);load.pop_back();}return false;
}

最后附赠打印代码,整合,给出源码:

#include<bits/stdc++.h>
using namespace std;string a, b, c, d;
string load;
map<char,char> m;
void align(char i){ //动了谁,就以谁为标注对齐4个字符串 switch(i){case 'A':case 'B':case 'E':case 'F':{c[2] = a[2]; c[4] = b[2];d[2] = a[4];  d[4] = b[4];break;}case 'C':case 'D':case 'G':case 'H':a[2] = c[2];   b[2] = c[4];a[4] = d[2];  b[4] = d[4];break;}
}
string move(char i){    //1=A,2=B,3=C,4=D,5=G,6=H,7=E,8=F   9-X=X的对面 string str;switch(i){case 'A':{char ch = a[0];for(int i=0;i<6;i++){a[i] = a[i+1];}a[6] = ch;str = "A";break;}case 'B':{char ch = b[0];for(int i=0;i<6;i++){b[i] = b[i+1];}b[6] = ch;str = "B";break;}case 'C':{char ch = c[6];for(int i=6;i>0;i--){c[i] = c[i-1];}c[0] = ch;str = "C";break;}case 'D':{char ch = d[6];for(int i=6;i>0;i--){d[i] = d[i-1];}d[0] = ch;str = "D";break;}case 'G':{char ch = d[0];for(int i=0;i<6;i++){d[i] = d[i+1];}d[6] = ch;str = "G";break;}case 'H':{char ch = c[0];for(int i=0;i<6;i++){c[i] = c[i+1];}c[6] = ch;str = "H";break;}case 'E':{char ch = b[6];for(int i=6;i>0;i--){b[i] = b[i-1];}b[0] = ch;str = "E";break;}case 'F':{char ch = a[6];for(int i=6;i>0;i--){a[i] = a[i-1];}a[0] = ch;str = "F";break;}}align(i);return str;
}
bool check(int delta){      //IDA*-剪枝 int sum = 0;string str;str = a[2]+c[3]+b[2]+b[3]+b[4]+d[3]+a[4]+a[3];for(int i=0;i<str.length()-1;i++){if(str[i]==str[i+1]) sum++;}return(sum/2>delta);
}
bool finish(){return(a[2]==c[3] && c[3]==b[2] && b[2]==b[3] && b[3]==b[4] && b[4]==d[3] && d[3]==a[4] && a[4]==a[3] && a[3]==a[2]);
}
void print(){       //debug用打印函数,release中不需要 cout<<"   A B"<<endl<<"   "<<a[0]<<" "<<b[0]<<endl<<"   "<<a[1]<<" "<<b[1]<<endl<<"H"<<c[0]<<c[1]<<c[2]<<c[3]<<c[4]<<c[5]<<c[6]<<"C"<<endl<<"   "<<a[3]<<" "<<b[3]<<endl<<"G"<<d[0]<<d[1]<<d[2]<<d[3]<<d[4]<<d[5]<<d[6]<<"D"<<endl<<"   "<<a[5]<<" "<<b[5]<<endl<<"   "<<a[6]<<" "<<b[6]<<endl<<"   F E"<<endl;cout<<endl;
}
bool IDA(int now, int deep){if(finish()){if(deep==0)  cout<<"no moves needed"<<endl;else    cout<<load<<endl;cout<<a[2]<<endl;return true;}if(check(deep-now))  return false;if(now == deep)      return false;for(char i='A';i<='H';i++){     //IDA-BFSgstring st;st = move(i);load += st;if(IDA(now+1,deep)) return true;move(m[i]);load.pop_back();}return false;
}
int main(){m['A'] = 'F'; m['B'] = 'E'; m['C'] = 'H'; m['D'] = 'G';m['F'] = 'A'; m['E'] = 'B'; m['H'] = 'C'; m['G'] = 'D';while(cin>>a[0]){if(a[0] == '0') return 0;cin                >>        b[0]>>a[1]>>        b[1]>>c[0]>>c[1]>>a[2]>>c[3]>>b[2]>>c[5]>>c[6]>>a[3]>>        b[3]>>d[0]>>d[1]>>a[4]>>d[3]>>b[4]>>d[5]>>d[6]>>a[5]>>        b[5]>>a[6]>>        b[6];c[2] = a[2];  c[4] = b[2];d[2] = a[4];  d[4] = b[4];for(int deep=0;;deep++){load = "";if(IDA(0,deep)) break;}}
}

最后记录一些要点:

1、IDA*就是在深度上进行优化的IDDFS,基本模块完全一致。

2、选择IDA*的条件:在深度选择上是可以优化的(埃及分数不行,因为答案不可预判)。

3、A*算法的构造:IDDFS均以算出结果为离开深度,因此A*的任务往往是去除过浅的情况。

IDA*学习笔记-HDU1667 The Rotation Game相关推荐

  1. ida pro学习笔记2022.3.19

    2022.3.19学习笔记 基本块:是一个不包含分支,从头执行到尾的最大指令序列.每个基本块,都有唯一的入口点(块中的第一条指令)和退出点(块中的最后一条指令). 基本块的第一条指令通常是分支指令的目 ...

  2. python可视化:matplotlib学习笔记

    信息可视化是数据分析的一块重要内容.这是一个探索性的过程.比如说,可以帮助我们坚定离群值,或者必要的数据转换,又或者是构建一个理想的模型.对于其他的一些领域,也可以进行web可视化.Python有许多 ...

  3. 【初阶】unity3d官方案例_太空射击SpacingShooter 学习笔记 显示分数时,如何让函数之间相互交流...

    [初阶]unity3d官方案例_太空射击SpacingShooter 学习笔记 显示分数时,如何让函数之间相互交流 一.关于 显示分数时,如何让函数之间相互交流 这是一个非常好的逻辑问题 1 思路:主 ...

  4. 游戏数据的捕捉(郁金香学习笔记)

    目录: 1.得到角色对象属性 2.得到吃药的CALL 3.得到快捷键CALL 4.得到选中怪物的ID地址 5.得到怪物列表 6.得到怪物属性 7.得到显示血条CALL 8.得到普通攻击CALL 9.得 ...

  5. Windows x64内核学习笔记(五)—— KPTI(未完待续)

    Windows x64内核学习笔记(五)-- KPTI(未完待续) KPTI 实验一:构造IDT后门并读取Cr3 参考资料 KPTI 描述:KPTI(Kernel page-table isolati ...

  6. Windows x64内核学习笔记(四)—— 9-9-9-9-12分页

    Windows x64内核学习笔记(四)-- 9-9-9-9-12分页 前言 9-9-9-9-12分页 实验一:线性地址转物理地址 页表基址 定位基址 PTE to PXE 实验二:通过页表基址定位各 ...

  7. 软件调试学习笔记(三)—— 调试事件的处理

    软件调试学习笔记(三)-- 调试事件的处理 要点回顾 调试事件的处理 实验一:实现简单调试器(创建进程) 实验二:分析异常来源 实验三:实现简单调试器(附加进程) 实验四:分析NtDebugActiv ...

  8. Windows异常学习笔记(五)—— 未处理异常

    Windows异常学习笔记(五)-- 未处理异常 要点回顾 最后一道防线 实验一:理解最后一道防线 实验二:新线程的最后一道防线 总结 UnhandledExceptionFilter 实验三:理解U ...

  9. Windows异常学习笔记(一)—— CPU异常记录模拟异常记录

    Windows异常学习笔记(一)-- CPU异常记录 基础知识 异常的分类 CPU异常 分析中断处理函数 _KiTrap00 分析 CommonDispatchException 总结 软件模拟异常 ...

最新文章

  1. 协议crc计算_从零了解modbus协议 第三篇
  2. linux修改文件夹及其下面所有文件的权限(文件夹权限)
  3. 《软件工程(第4版?修订版)》—第2章2.9节本章对研究人员的意义
  4. 【class2src】Decompiler
  5. 旋转研究(矩阵,欧拉角,四元数)
  6. 商业银行会计学内容概述
  7. java自动化开发_五大Java自动化测试框架
  8. “MATLAB拒绝访问”问题的解决方法
  9. CSS transform属性+js requestAnimationFrame函数实现旋转方块以及调整转动速度
  10. 阿里云centos7.4安装并部署svn1.10.0版本(配置多仓库,加入开机自启动)
  11. 用java制作扑克牌_JAVA入门第三季-简易扑克牌程序-个人编写-源代码(含截图)...
  12. C++游戏---皇后PK
  13. 小黄的日记,爱情本该如此
  14. 网易换肤第二篇:本地换肤实现!
  15. 如何设置,QQ邮箱新版界面
  16. java笔试题分类集锦
  17. Apple Watch卡住在苹果标志界面,该怎么解决?
  18. 2022 年初学者如何成为 iOS 开发者指南
  19. ASP.NET 使用日期控件
  20. 【bug】Failed at the node-sass@4.14.1 postinstall script(终于圆满解决)

热门文章

  1. 外行人都能看懂的WebFlux,错过了血亏
  2. 百款蓝牙耳机试用测评:这五款高性价比蓝牙耳机更值得入手
  3. 云计算技术基础 第3章 虚拟化技术(1)
  4. node sass编译报错 记录
  5. 不知名大牛二十六个月Android学习工作总结(整理)
  6. win7 旗舰版光驱不见了解决方案
  7. 海思3536解码G711音频
  8. 小米8 安装apk提示签名不一致
  9. it企业实习_IT行业实习报告3篇
  10. JSP与JavaMail(1)---JavaMail和JAF的介绍 (转)