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

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坐标差的绝对值之和为曼哈顿距离。

int manhattan(int a[][size])//计算曼哈顿距离,小等于实际总步数
{
int i,j,cost=;
for(i=;i<size;i++)
for(j=;j<size;j++)
{
int w=map[i][j];
cost+=abs(i-goal[w][])+abs(j-goal[w][]);
}
return cost;
}

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

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

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

//15数码问题
#include<iostream>
#include<cstdlib>
#include<cmath>
#define size 4
using namespace std; int move[][]={{-,},{,-},{,},{,}};//上,左,右,下增量
char op[]={'U','L','R','D'};
int map[size][size],map2[size*size],limit,path[];
int flag,length;
//int goal_st[3][3]={{1,2,3},{4,5,6},{7,8,0}};//目标状态
//goal存储目标位置,即0存在(3,3),1存在(0,0)...
int goal[][]= {{,},{,},{,}, {,},
{,},{,},{,}, {,},
{,},{,},{,}, {,},
{,},{,},{,}, {,}}; int h(int a[size*size])//求逆序数
{
int i,j,num,w,x,y;
num=;
for(i=;i<size*size;i++)
{
if(a[i]==)
w=i;
for(j=i+;j<size*size;j++)
{
if(a[i]>a[j])
num++;
}
}
x=w/size;
y=w%size;
num+=abs(x-)+abs(y-);
if(num%==)
return ;
else
return ;
} int manhattan(int a[][size])//计算曼哈顿距离,小等于实际总步数
{
int i,j,cost=;
for(i=;i<size;i++)
for(j=;j<size;j++)
{
int w=map[i][j];
cost+=abs(i-goal[w][])+abs(j-goal[w][]);
}
return cost;
} void swap(int*a,int*b)
{
int tmp;
tmp=*a;
*a=*b;
*b=tmp;
} void dfs(int sx,int sy,int dep,int pre_move)//sx,sy是空格的位置
{
int i,j,nx,ny;
if(flag)
return;
int dv=manhattan(map);
if(dep==limit)
{
if(dv==)
{
flag=;
length=dep;
return;
}
else
return;
}
else if(dep<limit)
{
if(dv==)
{
flag=;
length=dep;
return;
}
}
for(i=;i<;i++)//4个方向尝试
{
if(i+pre_move==&&dep>)//不和上一次移动方向相反,对第二步以后而言
continue;
nx=sx+move[i][];
ny=sy+move[i][];
if(<=nx && nx<size && <=ny&&ny<size)//如果可以移动
{
swap(&map[sx][sy],&map[nx][ny]);//交换两位置
int p=manhattan(map);
if(p+dep<=limit&&!flag)
{
path[dep]=i;
dfs(nx,ny,dep+,i);
if(flag)
return;
}
swap(&map[sx][sy],&map[nx][ny]);
}
}
} int main()
{
freopen("Puzzle15.in","r",stdin);
freopen("Puzzle15.out","w",stdout);
int i,j,k,l,m,n,sx,sy;
char c,g;
i=;
scanf("%d",&n);
while(n--)
{
flag=;length=;
memset(path,-,sizeof(path));
for(i=;i<;i++)
{
scanf("%d",&map2[i]);
if(map2[i]==)
{
map[i/size][i%size]=;
sx=i/size;sy=i%size;
}
else
{
map[i/size][i%size]=map2[i];
}
}
if(h(map2)==)//该状态可达
{
limit=manhattan(map);
while(!flag&&length<=)//题中要求50步之内到达
{
dfs(sx,sy,,);
if(!flag)
limit++; //得到的是最小步数
}
if(flag)
{
for(i=;i<length;i++)
printf("%c",op[path[i]]);
printf("\n");
}
}
else if(!h(map2)||!flag)
printf("This puzzle is not solvable.\n");
}
return ;
}

终于口胡完了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. (六)授权(下):自定义permission

    一.Authorizer.PermissionResolver及RolePermissionResolver Authorizer的职责是进行授权(访问控制),是Shiro API中授权核心的入口点, ...

  2. EF6.0中出现未找到具有固定名称“System.Data.SqlClient”的 ADO.NET提供程序的实体框架提供程序解决办法

    在多工程项目中,由于EF封装在某一个工程里,那么该项目用于EF相关类库 EntityFramework.dll,以及EntityFramework.SqlServer.dll的引用 那么你一个启动工程 ...

  3. 七、Flex 布局

    布局的传统解决方案,基于盒状模型,依赖 display 属性 + position属性 + float属性.它对于那些特殊布局非常不方便,比如,垂直居中就不容易实现. 2009年,W3C 提出了一种新 ...

  4. 最快得到MYSQL两个表的差集

    Mysql里不外乎就是 子查询 和 连接 两种方式. 设第一个表为table1, 第二个为table2, table1包含table2. sql为: //子查询 select table1.id fr ...

  5. linux安装tmux分屏插件

    linuxtmux分屏 一.安装tmux 二.基本使用 三.鼠标操作 一.安装tmux yum install -y tmux TMUX2版本以下 二.基本使用 使用tmux一般使用命令和快捷键来操作 ...

  6. php实现命令行里输出带颜色文字

    今天执行composer的时候看到命令窗口出现的提示里面有的关键性部分带有颜色,于是很好奇研究了一下,在这里记录下来 其实在命令行输出带颜色字体主要是使用的 ANSI 转义字符实现的,我们先看个例子: ...

  7. Servlet实现图片文件上传

    1.首先要导入以下两个jar包: commons-fileupload-1.2.1.jarcommons-io-1.4.jar 2.jsp文件:index.jsp <%@ page langua ...

  8. elment 中tree组件展开所有和收缩所有

    upAll () { // 全部展开 遍历变成true let self = this; // 将没有转换成树的原数据 let treeList = this.sourceData; for (let ...

  9. Linux下安装php报错:libxml2 not found. Please check your libxml2 installation

    ubuntu/debian: apt-get install libxml2-dev centos/redhat: yum install libxml2-devel

  10. vue嵌套数据多层级里面的数据不自动更新渲染dom

    可以尝试手动调用更新 主要代码: vm.$forceUpdate() 官网