这一次是交流测试?边交流边测试(滑稽

15数码问题

大家应该都玩过这个15数码的游戏吧,就在桌面小具库那里面哦。

一看到这个题就知道要GG,本着能骗点分的原则输出了 t 个无解,本来以为要爆零,没想到这个题数据是真的水,全输出无解能骗到40分,某些大佬输出样例了又骗到了20分 QwQ~;

本题对应着洛谷的UVA10181 15-Puzzle Problem ,省选难度,果然不简单QwQ~,_rqy 讲了一番,听得糊里糊涂。

这个题正解是搜索(貌似是废话,都给了15s的时间了),但是还是需要一些剪枝的。

剪枝一:未做此题先判其解

看着这四四方方的格子,不大好搞啊,所以我们可以抽象的把这些数字给整到一行去,比如我们把上面的情况给整成这样:

所以我们最后要搞成这样是吧:

所以我们可以换个角度来思考问题:

考虑到最终目标无逆序对,所以我们可以将问题转化为:求交换多少次能使原长串的逆序对给弄成0!

因为我们每交换一下相邻的两个数字,整个序列的逆序对的奇偶性就会改变。(逆序对数是偶数就显偶性,是奇数就显奇性)

所以如果原序列的逆序对是奇性的话,我们至少要进行奇数次操作;是偶性的话,我们至少要进行偶数次操作;

然后我们可以求出原序列的逆序对数,先判断答案的奇偶性;

于是这里用到一个定理:设初始状态0所在的行数为i,目标状态0所在的行数为j,两者之差的绝对值为k。若k为奇数,则两个矩阵相应的逆序数的奇偶性相异才有解。若k为偶数,则两个矩阵的逆序数必须相同才有解。不是上述两种情况即为无解。通过初始判定就可以不用搜索就能直接否定无解情况。

剪枝二:从最小步数开始搜

我们每进行一步操作,对应的数字会往目标值更近一步。

假如我们这个位置有个 12,我们一步一步得将它移到目标位置,那么我们最少步数就是 3 :

怎么算呢?就是终点的横纵坐标再减去起点的横纵坐标的绝对值再相加便可以得到,这个距离叫曼哈顿距离。(大雾

标准定义(好像跟我口胡得差不多):

曼哈顿距离:在标准坐标系中,两点的x坐标差的绝对值与y坐标差的绝对值之和为曼哈顿距离。

  1. int manhattan(int a[][size])//计算曼哈顿距离,小等于实际总步数
  2. {
  3. int i,j,cost=;
  4. for(i=;i<size;i++)
  5. for(j=;j<size;j++)
  6. {
  7. int w=map[i][j];
  8. cost+=abs(i-goal[w][])+abs(j-goal[w][]);
  9. }
  10. return cost;
  11. }

作用:求出初始矩阵与目标矩阵对应值得曼哈顿距离并求和(除去0)得到的值为评估值,写成函数即为评估函数。该值为从初始状态到目标状态所要经过的最小步数,实际步数只会大于等于该值。

算法介绍:这次使用的算法是IDA*。我们首先是用逆序数进行判定是否有解,有解才进行搜索。有解的话,则先得到评估函数的初始值,该值为最小步数,递归深度(步数)必然大于等于这个初始值limit。我们先按深度搜索寻遍该深度的所有情况,看是否能找到解,有解则该解是最优解。若没解,则将深度的限制条件limit加1,再次递归下一层深度的所有情况,有解即为最优解,无解则继续将深度限制条件limit加1,这样不停循环直到某个深度maxLevel,则放弃寻找,因为在maxLevel步中没有找到,继续找下去时间花销太高,故放弃寻找。这就是IDA*中ID的意思,迭代加深。其中,算法在递归中使用了当前递归深度level,用level+评估函数(当前状态到目标状态至少需要的步数)<=limit作为剪枝条件,不满足该条件的在该分支上肯定无解。这样我们就可以找到在maxLevel步以内的最优解。

So,完整标程代码如下:(大雾

  1. //15数码问题
  2. #include<iostream>
  3. #include<cstdlib>
  4. #include<cmath>
  5. #define size 4
  6. using namespace std;
  7.  
  8. int move[][]={{-,},{,-},{,},{,}};//上,左,右,下增量
  9. char op[]={'U','L','R','D'};
  10. int map[size][size],map2[size*size],limit,path[];
  11. int flag,length;
  12. //int goal_st[3][3]={{1,2,3},{4,5,6},{7,8,0}};//目标状态
  13. //goal存储目标位置,即0存在(3,3),1存在(0,0)...
  14. int goal[][]= {{,},{,},{,}, {,},
  15. {,},{,},{,}, {,},
  16. {,},{,},{,}, {,},
  17. {,},{,},{,}, {,}};
  18.  
  19. int h(int a[size*size])//求逆序数
  20. {
  21. int i,j,num,w,x,y;
  22. num=;
  23. for(i=;i<size*size;i++)
  24. {
  25. if(a[i]==)
  26. w=i;
  27. for(j=i+;j<size*size;j++)
  28. {
  29. if(a[i]>a[j])
  30. num++;
  31. }
  32. }
  33. x=w/size;
  34. y=w%size;
  35. num+=abs(x-)+abs(y-);
  36. if(num%==)
  37. return ;
  38. else
  39. return ;
  40. }
  41.  
  42. int manhattan(int a[][size])//计算曼哈顿距离,小等于实际总步数
  43. {
  44. int i,j,cost=;
  45. for(i=;i<size;i++)
  46. for(j=;j<size;j++)
  47. {
  48. int w=map[i][j];
  49. cost+=abs(i-goal[w][])+abs(j-goal[w][]);
  50. }
  51. return cost;
  52. }
  53.  
  54. void swap(int*a,int*b)
  55. {
  56. int tmp;
  57. tmp=*a;
  58. *a=*b;
  59. *b=tmp;
  60. }
  61.  
  62. void dfs(int sx,int sy,int dep,int pre_move)//sx,sy是空格的位置
  63. {
  64. int i,j,nx,ny;
  65. if(flag)
  66. return;
  67. int dv=manhattan(map);
  68. if(dep==limit)
  69. {
  70. if(dv==)
  71. {
  72. flag=;
  73. length=dep;
  74. return;
  75. }
  76. else
  77. return;
  78. }
  79. else if(dep<limit)
  80. {
  81. if(dv==)
  82. {
  83. flag=;
  84. length=dep;
  85. return;
  86. }
  87. }
  88. for(i=;i<;i++)//4个方向尝试
  89. {
  90. if(i+pre_move==&&dep>)//不和上一次移动方向相反,对第二步以后而言
  91. continue;
  92. nx=sx+move[i][];
  93. ny=sy+move[i][];
  94. if(<=nx && nx<size && <=ny&&ny<size)//如果可以移动
  95. {
  96. swap(&map[sx][sy],&map[nx][ny]);//交换两位置
  97. int p=manhattan(map);
  98. if(p+dep<=limit&&!flag)
  99. {
  100. path[dep]=i;
  101. dfs(nx,ny,dep+,i);
  102. if(flag)
  103. return;
  104. }
  105. swap(&map[sx][sy],&map[nx][ny]);
  106. }
  107. }
  108. }
  109.  
  110. int main()
  111. {
  112. freopen("Puzzle15.in","r",stdin);
  113. freopen("Puzzle15.out","w",stdout);
  114. int i,j,k,l,m,n,sx,sy;
  115. char c,g;
  116. i=;
  117. scanf("%d",&n);
  118. while(n--)
  119. {
  120. flag=;length=;
  121. memset(path,-,sizeof(path));
  122. for(i=;i<;i++)
  123. {
  124. scanf("%d",&map2[i]);
  125. if(map2[i]==)
  126. {
  127. map[i/size][i%size]=;
  128. sx=i/size;sy=i%size;
  129. }
  130. else
  131. {
  132. map[i/size][i%size]=map2[i];
  133. }
  134. }
  135. if(h(map2)==)//该状态可达
  136. {
  137. limit=manhattan(map);
  138. while(!flag&&length<=)//题中要求50步之内到达
  139. {
  140. dfs(sx,sy,,);
  141. if(!flag)
  142. limit++; //得到的是最小步数
  143. }
  144. if(flag)
  145. {
  146. for(i=;i<length;i++)
  147. printf("%c",op[path[i]]);
  148. printf("\n");
  149. }
  150. }
  151. else if(!h(map2)||!flag)
  152. printf("This puzzle is not solvable.\n");
  153. }
  154. return ;
  155. }

终于口胡完了QwQ~(话说我竟然除了代码其他好像都懂了)

2019.7.9 校内测试 T3 15数码问题的更多相关文章

  1. 2019.6.28 校内测试 T3 【音乐会】道路千万条

    大眼一看最下面的题意解释的话,发现这和洛谷P1310表达式的值挺像的,大概都是给定一些运算符号,让最后的表达式为true的概率,为false的概率啥的QwQ~: 然后这个题嘛?就是在所有的运算符中提溜 ...

  2. 2019.7.9 校内测试 T2 极值问题

    这一次是交流测试?边交流边测试(滑稽 极值问题 乍一看这是一道数学题,因为1e9的数据让我暴力的心退却. 数学又不好,不会化简式子嘞,咋办? 不怕,咱会打表找规律.(考场上真的是打表找出了规律,打表打 ...

  3. 2019.7.9 校内测试 T1挖地雷

    这一次是交流测试?边交流边测试(滑稽 挖地雷 这个题是一个递推问题. 首先我们看第一个格子,因为它只影响了它的上面和右上面这两个地方是否有雷. 我们可以分3种情况讨论: 1. 第一个格子的数字是2: ...

  4. 2019.6.24 校内测试 NOIP模拟 Day 2 分析+题解

    看到Day 2的题真的想打死zay了,忒难了QwQ~ T1 江城唱晚 这明显是个求方案数的计数问题,一般的套路是DP和组合数学. 正如题目中所说,这个题是一个 math 题.      ----zay ...

  5. 2019.6.20 校内测试 NOIP模拟 Day 1 分析+题解

    这次是zay神仙给我们出的NOIP模拟题,不得不说好难啊QwQ,又倒数了~ T1 大美江湖 这个题是一个简单的模拟题.   ----zay 唯一的坑点就是打怪的时候计算向上取整时,如果用ceil函数一 ...

  6. 18清明校内测试T3

    扫雷(mine) Time Limit:1000ms   Memory Limit:128MB 题目描述 rsy最近沉迷于一款叫扫雷的游戏. 这个游戏是这样的.一开始网格上有n*m个位置,其中有一些位 ...

  7. 2019.6.28 校内测试 T4 【音乐会】达拉崩吧·上

    考试的一道附加题~ 一看题目描述:把区间[l,r]里每个数异或上x,求区间[l,r]里所有数的异或和,这明显的要用数据结构或RMQ吧. 恩,所以正解就是线段树啦,至于树状数组行与否,不知道~ wate ...

  8. 2019.6.28 校内测试 T2 【音乐会】二重变革

    看到这个题之后,一个很暴力很直接的想法就是贴上题目中的代码然后交上去走人,但是很显然这是会TLE+MLE的,想想谁会这么傻把主要代码给你QwQ~: 其实这段代码是想告诉你一件事:用序列中的大数减去小数 ...

  9. 2019.6.28 校内测试 T1 Jelly的难题1

    这题面有点难理解,建议直接跳到题意解释那一部分(虽然我觉得解释的不大对,但按照解释来做确实能AC): 按照“题意解释”的思路来思考这个题,那么就十分的简单了: 1.首先要读入这个字符矩阵,可以用cin ...

随机推荐

  1. Mysql 集群环境搭建

    在上一篇文章中已经详细的写了关于Mysql的安装步骤.这一篇文章在上一篇文章的基础之上接着写集群的安装与部署. 安装地址:https://www.cnblogs.com/ming-blogs/p/10 ...

  2. [前端]多线程在前端的应用——Javascript的线程

    JavaScript 是单线程.异步.非阻塞.解释型脚本语言.JavaScript 的设计就是为了处理浏览器网页的交互(DOM操作的处理.UI动画等),决定了它是一门单线程语言.如果有多个线程,它们同 ...

  3. [JZOJ5897]密匙--哈希骚操作

    [JZOJ5897]密匙--哈希骚操作 题目链接 太懒了自行Google 前置技能 二分/倍增求LCP e.g TJOI2017DNA 分析 这题看了样例解释才知道什么意思 本以为自己身为mo法师蛤希 ...

  4. siamese跟踪论文思考

    转载自:https://zhuanlan.zhihu.com/p/34222060 通过作者在专栏里面放的几张响应图我们可以看到:SiamFC并不能区分不同的物体,图片上所有具有语义信息的物体都会得到 ...

  5. 基于【 centos7】二 || 系统时间与网络时间同步

    # date // 查看系统时间 #hwclock // 查看硬件时间 # yum -y install ntp ntpdate 安装ntpdate工具 # ntpdate cn.pool.ntp.o ...

  6. Python 数据类型和控制结构

    Python是一门脚本语言,我也久闻大名,但正真系统的接触学习是在去年(2013)年底到今年(2014)年初的时候.不得不说的是Python的官方文档相当齐全,如果你是在Windows上学习Pytho ...

  7. linux(centos)下安装supervisor进程管理工具

    在接触supervisor进程管理工具之前,使用springboot打包部署到linux服务器的流程是这样子的,如下图所示: 上图展示的就是最一般的流程,如果项目是小项目或者demo可以这样子去部署, ...

  8. opengl 4.5 中文api 链接

    https://www.cnblogs.com/wiki3d/p/opengl_a.html

  9. python中常用的九种预处理方法

    本文总结的是我们大家在python中常见的数据预处理方法,以下通过sklearn的preprocessing模块来介绍; 1. 标准化(Standardization or Mean Removal ...

  10. git rev-list 按照时间来列出两个 commit id 之间的相差数

    git rev-list 按照时间来列出两个 commit id 之间的相差数 git rev-list: Lists commit objects in reverse chronological ...