【题解】Luogu P2730 魔板
蒟蒻的第一道蓝题……好像也没有蓝的程度
一篇无STL的超弱题解(入门写法无误了QAQ
传送门
很经典的一道BFS
这是初始状态。

操作A

操作B

操作C

思路1 不使用cantor展开的情况
1. 对于存储这个操作序列
- 一个没有什么用的空间小优化
(然后时间就炸了)
存储一个字符,我们都知道需要1个Byte。那么我们存储一个魔板序列时,就需要8个Byte。
魔板的状态有8!=40320种,那我们在不断的存储许多新的状态时,需要预先开至少8*40320个字节的空间。
如果我们使用int类型进行存储,每个int类型只需要4Byte,那么就只需要4*40320个字节。(也没优化多少啊)
2.进行扩展
建立一个队列,初始的状态为队列[1][1]=12345678
接着我们判断,如果初始状态已经等于目标状态,直接输出0,即不需要操作
这个队列中的每个状态,我们需要三个空间,分别存储魔板的状态,魔板的父亲位置(就是现在魔板的状态是由队列中哪个下标的状态进行一次变化而来的),以及魔板是由魔板父亲状态经过什么操作来达到现在状态的。我们可以将三种操作简写为1,2,3。
对魔板状态进行扩展。即将魔板分别进行三种操作。
PS:由于我们存储了int类型,可以手推一下三种操作的公式。推荐参考上面三幅图片。
每一种操作结束后,判断现在魔板状态是否在之前有出现过。(判重)
PS:判重不使用康托展开,开87654322大的数组即可。亲测可过。
一旦判断已经达到目标状态,输出。
3.输出
我们在队列中已经存储过魔板的父亲位置。现在要利用这个位置来逆推我们的步骤。
建立一个way数组。不断的存储父亲位置。
逆向输出。输出对应位置上的操作。
详情查看代码。
请从变量定义及主函数开始阅读
#include <cstdio>
#include <cmath>//pow函数的头文件
int tl[100000][3],maxn=100000;//tl是我们存储状态的队列。tl[i][1]是指第i个情况的魔板状态,tl[i][0]是第i个情况的父亲位置,t[i][2]是指第i个情况的父亲的操作(能得到i情况)
int now=12345678,finish=0;//初始状态&目标状态
int way[100000];//用于最后逆推过程的时候存储父亲位置
bool book[87654322]={};//判重(真的不会M可能是因为最开始存int
//下面三个操作函数都很乱,建议手算公式<qwq>
void A(int j)//A操作!
{
int qwq=0;
for (int i=8;i>=1;i--)
{
qwq+=((tl[j][1]%10)*pow(10,i-1));
tl[j][1]/=10;
}
tl[j][1]=qwq;
return;
}
void B(int j)//B操作!
{
tl[j][1]=(tl[j][1]%100000/10000)*10000000+(tl[j][1]/100000)*10000+tl[j][1]%1000*10+tl[j][1]%10000/1000;
}
void C(int j)//C操作!
{
tl[j][1]=tl[j][1]-((tl[j][1]/100000)%100)*100000-(tl[j][1]%1000)/10*10+tl[j][1]/1000000%10*100000+tl[j][1]/100000%10*100+tl[j][1]/100%10*10+tl[j][1]/10%10*1000000;//2367->7236
}
///////////////////////////////////分割线正常阅读
bool look_for(int n)//判重,即新情况的模版状态有没有在队列中出现过
{
return book[ tl[n][1] ];
}
bool found(int w)//判断是否达到目标状态
{
if ( tl[w][1] == finish )
{
return true;
}
return false;
}
void print(int zt)//打印函数。zt是指队列第zt种情况
{
if ( zt == 1 )//请联系start函数的第一句“if……”理解
{
printf("0");
return;
}
int k=0,z=zt;
while (z>0)//逆推!比较难理解可以试着手画一下示意
{
k++;
way[k] = z;
z = tl[z][0];
}
printf("%d\n",k-1);
for (int i=k-1;i>0;i--)
{
z = way[i];
printf("%c",tl[z][2]+'A'-1);//转化为字符输出!原本是用123表示ABC操作
}
return;
}
void start()//开始啦!
{
if ( found(1) )//如果初始状态==目标状态
{
print(1);
return;
}
int i=1,j=2;
while ( i<j && j<maxn )//仍有状态可以扩展&未溢出上限
{
for (int k=1;k<=3;k++)
{
tl[j][1]=tl[i][1];//预入队!
if (k==1){ A(j); }//A操作
if (k==2){ B(j); }//B操作
if (k==3){ C(j); }//C操作
if ( ! look_for(j) )//如果这种状态在之前没有出现过
{
tl[j][0]=i;//tl[j][0]:我父亲叫i!
tl[j][2]=k;//tl[j][2]:我是i和k的孩子!
if (found(j))//如果已经达到了目标状态
{
print(j);//输出!
return;
}
else { book[ tl[j][1] ]=1; j++; }//标记这种状态已经出现过&队尾+1
}
}
i++;//一种情况的扩展完毕,队首+1
}
}
int main()
{
for (int i=8;i>=1;i--)//输入,转换成int
{
int x;
scanf("%d",&x);
finish+=(x*pow(10,i-1));
}
tl[1][1]=now; tl[1][0]=0;//队列的第一个状态就是初始状态now,ta的父亲没有了……
start();//开始吧!
return 0;//圆满的结束……
}
思路2 cantor展开
什么是康托展开?
对于n的一个排列,我们可以通过康托展开,映射出这个排列在n的全排列中占第几小。
那么我们本来使用87654322大的数组来标记出现过的状态,这时就只需要开8!=40320大的数组进行标记。
注意!使用康托展开需要将数位一位位分离。请慎重考虑要使用什么类型存储。推荐使用字符存储。
这里提供一个正向康托展开的代码。 具体可以戳这里
int factorial[11]={1,1,2,6,24,120,720,5040,40320,362880};//0-10的阶乘!
int cantor(int a[]){//正向康托展开,求排列a[]在1~8的全排列中占第几小
int ans=0,sum=0;
for(int i=1;i<=7;i++)//最后一位不用计算(0*0!)
{
for(int j=i+1;j<=8;j++)//寻找这个排列的第i位的后面有多少个数小于ta
{
if(a[j]<a[i]) sum++;
}
ans+=sum*factorial[8-i];//ans+=sum*(8-1)!
sum=0;//计数归零
}
return ans+1;//这里计算的ans是比a[]排列小的排列的个数,所以a[]排列是第ans+1小的!
}
其余的思路基本是相同的。不过对于ABC三种操作请使用swap来实现。
蒟蒻的第一篇题解qwq
准备被残忍拒绝
【题解】Luogu P2730 魔板的更多相关文章
- 洛谷 P2730 魔板 Magic Squares 解题报告
P2730 魔板 Magic Squares 题目背景 在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板.这是一张有8个大小相同的格子的魔板: 1 2 3 4 8 7 6 5 题目描述 ...
- 洛谷 P2730 魔板 Magic Squares
P2730 魔板 Magic Squares 题目背景 在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板.这是一张有8个大小相同的格子的魔板: 1 2 3 4 8 7 6 5 题目描述 ...
- 洛谷P2730 魔板 [广搜,字符串,STL]
题目传送门 魔板 题目背景 在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板.这是一张有8个大小相同的格子的魔板: 1 2 3 4 8 7 6 5 题目描述 我们知道魔板的每一个方格都有 ...
- P2730 魔板 Magic Squares
题目背景 在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板.这是一张有8个大小相同的格子的魔板: 1 2 3 4 8 7 6 5 题目描述 我们知道魔板的每一个方格都有一种颜色.这8种颜 ...
- [洛谷P2730] 魔板 Magic Squares
洛谷题目链接:魔板 题目背景 在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板.这是一张有8个大小相同的格子的魔板: 1 2 3 4 8 7 6 5 题目描述 我们知道魔板的每一个方格都 ...
- P2730 魔板 Magic Squares (搜索)
题目链接 Solution 这道题,我是用 \(map\) 做的. 具体实现,我们用一个 \(string\) 类型表示任意一种情况. 可以知道,排列最多只有 \(8!\) 个. 然后就是直接的广搜了 ...
- 洛谷 - P2730 - 魔板 Magic Squares - bfs
写状态转移弄了很久,老了,不记得自己的数组是怎么标号的了. #include <bits/stdc++.h> using namespace std; #define ll long lo ...
- 哈希+Bfs【P2730】 魔板 Magic Squares
没看过题的童鞋请去看一下题-->P2730 魔板 Magic Squares 不了解康托展开的请来这里-->我这里 至于这题为什么可以用康托展开?(瞎说时间到. 因为只有8个数字,且只有1 ...
- 【题解】魔板—洛谷P1275。
话说好久没更博了. 最近学了好多知识懒的加进来了. 有幸认识一位大佬. 让我有了继续更博的兴趣. 但这是一个旧的题解. 我在某谷上早就发过的. 拿过来直接用就当回归了吧. 其实这道题有一个特别关键的思 ...
随机推荐
- POI导出Excel发现不可读取的内容
环境说明:MyEclipse Tomcat7.0 通过后台查询数据,导出Excel在打开时会出现以下提示: 点击否,则不显示任何内容,点击是,弹出 查看修改记录为: 通过WPS打开不会出现任何提示,可 ...
- 五、Sql Server 基础培训《进度5-数据类型(知识点+实际操作)》
知识点: ================================================= ============================================= ...
- Linux下的awk文本分析命令实例(一)
1. 入门实例1.1 显示最近登录的5个帐号: [root@localhost ~]# | awk '{print $1}' root root root root reboot 1.2 如果只是显 ...
- vue2.0 --- vuex (一)
之前做vue项目中没有使用vuex 一直使用vue-router 组件.路由一直的转换,烦不胜烦 今天研究一下vuex vuex是什么: vuex是专门为vue.js应用程序开发的状态管理模式. 解 ...
- js运用5
js数据类型具体分析 1.基础类型:string number boolean null undefined 2.引用类型:object==> json array ...
- Laravel开发采坑系列问题
2017年12月22日17:40:03 不定时更新 版本5.4.X 一下是可能会遇到的坑 1,必须的写路由转发才能访问控制器,当然你可以自动路由访问,但是需要些匹配规则,其实还是转发了 好多人讨论过自 ...
- .NET Core开发日志——OData
简述 OData,即Open Data Protocol,是由微软在2007年推出的一款开放协议,旨在通过简单.标准的方式创建和使用查询式及交互式RESTful API. 类库 在.NET Core中 ...
- vue computed的执行问题
1.在new Vue()的时候,vue\src\core\instance\index.js里面的_init()初始化各个功能 function Vue (options) { if (process ...
- HTML基础之HTML标签-html header(meta,title) html body(p,br,h,form,div,span,input,lable)
摘自:http://www.imdsx.cn/index.php/2017/07/27/html0/ 一.HTML标签 <!DOCTYPE html> <!--标准的html规则,类 ...
- spark-sql中的DataFrame文件格式转储示例
SparkConf sparkConf = new SparkConf() // .setMaster("local") .setAppName("DataFrameTe ...