洛谷OJ P1379 八数码难题 解题报告

洛谷OJ P1379 八数码难题 解题报告

by MedalPluS

题目描述

  在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

  输入格式

  输入初试状态,一行九个数字,空格用0表示

  输出格式

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)

  分析

其实就是个裸的BFS,但是可以拓展很多知识点(有人说不做这题的人的OI人生不完美)

暴力BFS的话会被卡,因为要搜索的状态实在太多了

所以这里有两种优化:

1.启发式搜索

我们假设f=g+h,那么g可以设为已经挪动的步数,h为不在位将牌个数,这样就构建了一个估价函数

然后直接写就好

2.双向BFS

我们知道起点和终点,直接双向就好

另外,这里应该如何判重呢?其实很简单,下面引入康托展开:http://blog.csdn.net/u010372095/article/details/9904497

然后就利用康托值进行判重就好

  代码

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <queue>
  4 #include <cstdlib>
  5 using namespace std;
  6
  7 #define rep(i,l,r) for(i=l;i<=r;i++)
  8 const int maxn=10;
  9 const int maxm=1000001;//9!*8+8!*7...<=5000001
 10
 11 struct node{
 12     int a[maxn][maxn];
 13     short int g;
 14     node(){g=0;}
 15 }Head,Tail;
 16
 17 short int vis_f[maxm],vis_b[maxm];
 18 int tmps[maxn];
 19 queue<node> f,b;
 20
 21 int fact(int x){
 22     int ans;
 23     for(ans=1;x>=2;x--)ans*=x;
 24     return ans;
 25 }
 26
 27 int Cantor(node now){//Cantor_Expand
 28     int i,j,t=0,res=0;
 29     rep(i,1,3)
 30       rep(j,1,3)
 31         tmps[t++]=now.a[i][j];
 32     t=0;
 33     rep(i,0,8)
 34     {
 35         t=0;
 36         rep(j,i+1,8)
 37           if(tmps[j]<tmps[i])
 38             t++;
 39         res+=t*fact(9-i-1);
 40     }
 41     return res;
 42 }
 43
 44 void init_Front(){
 45     Head.a[1][1]=1;Head.a[1][2]=2;Head.a[1][3]=3;
 46     Head.a[2][1]=8;Head.a[2][2]=0;Head.a[2][3]=4;
 47     Head.a[3][1]=7;Head.a[3][2]=6;Head.a[3][3]=5;
 48     Head.g=0;
 49 }
 50
 51 void init_Back(){
 52     int i,j;
 53     rep(i,1,3)
 54       rep(j,1,3)
 55         scanf("%1d",&Tail.a[i][j]);
 56 }
 57
 58 void init(){
 59     memset(vis_f,-1,sizeof(vis_f));
 60     memset(vis_b,-1,sizeof(vis_b));
 61     init_Front();
 62     init_Back();
 63 }
 64
 65 void expand_f(){
 66     node head,tmp;
 67     head=f.front();
 68     f.pop();
 69     int i,j,sx,sy;
 70     rep(i,1,3)
 71       rep(j,1,3)
 72         if(!head.a[i][j])
 73         {
 74           sx=i;
 75           sy=j;
 76           break;
 77         }
 78     tmp=head;
 79     if(sx-1>0){
 80         swap(tmp.a[sx-1][sy],tmp.a[sx][sy]);
 81         tmp.g++;
 82         if(vis_b[Cantor(tmp)]!=-1){cout<<tmp.g+vis_b[Cantor(tmp)];exit(0);}
 83         if(vis_f[Cantor(tmp)]==-1){
 84           vis_f[Cantor(tmp)]=tmp.g;
 85           f.push(tmp);
 86         }
 87     }
 88     tmp=head;
 89     if(sx+1<=3){
 90         swap(tmp.a[sx+1][sy],tmp.a[sx][sy]);
 91         tmp.g++;
 92         if(vis_b[Cantor(tmp)]!=-1){cout<<tmp.g+vis_b[Cantor(tmp)];exit(0);}
 93         if(vis_f[Cantor(tmp)]==-1){
 94           vis_f[Cantor(tmp)]=tmp.g;
 95           f.push(tmp);
 96         }
 97     }
 98     tmp=head;
 99     if(sy-1>0){
100         swap(tmp.a[sx][sy-1],tmp.a[sx][sy]);
101         tmp.g++;
102         if(vis_b[Cantor(tmp)]!=-1){cout<<tmp.g+vis_b[Cantor(tmp)];exit(0);}
103         if(vis_f[Cantor(tmp)]==-1){
104           vis_f[Cantor(tmp)]=tmp.g;
105           f.push(tmp);
106         }
107     }
108     tmp=head;
109     if(sy+1<=3){
110         swap(tmp.a[sx][sy+1],tmp.a[sx][sy]);
111         tmp.g++;
112         if(vis_b[Cantor(tmp)]!=-1){cout<<tmp.g+vis_b[Cantor(tmp)];exit(0);}
113         if(vis_f[Cantor(tmp)]==-1){
114           vis_f[Cantor(tmp)]=tmp.g;
115           f.push(tmp);
116         }
117     }
118 }
119
120 void expand_b(){
121     node head,tmp;
122     head=b.front();
123     b.pop();
124     int i,j,sx,sy;
125     rep(i,1,3)
126       rep(j,1,3)
127         if(!head.a[i][j])
128         {
129           sx=i;
130           sy=j;
131           break;
132         }
133     tmp=head;
134     if(sx-1>0){
135         swap(tmp.a[sx-1][sy],tmp.a[sx][sy]);
136         tmp.g++;
137         if(vis_f[Cantor(tmp)]!=-1){cout<<tmp.g+vis_f[Cantor(tmp)];exit(0);}
138         if(vis_b[Cantor(tmp)]==-1){
139           vis_b[Cantor(tmp)]=tmp.g;
140           b.push(tmp);
141         }
142     }
143     tmp=head;
144     if(sx+1<=3){
145         swap(tmp.a[sx+1][sy],tmp.a[sx][sy]);
146         tmp.g++;
147         if(vis_f[Cantor(tmp)]!=-1){cout<<tmp.g+vis_f[Cantor(tmp)];exit(0);}
148         if(vis_b[Cantor(tmp)]==-1){
149           vis_b[Cantor(tmp)]=tmp.g;
150           b.push(tmp);
151         }
152     }
153     tmp=head;
154     if(sy-1>0){
155         swap(tmp.a[sx][sy-1],tmp.a[sx][sy]);
156         tmp.g++;
157         if(vis_f[Cantor(tmp)]!=-1){cout<<tmp.g+vis_f[Cantor(tmp)];exit(0);}
158         if(vis_b[Cantor(tmp)]==-1){
159           vis_b[Cantor(tmp)]=tmp.g;
160           b.push(tmp);
161         }
162     }
163     tmp=head;
164     if(sy+1<=3){
165         swap(tmp.a[sx][sy+1],tmp.a[sx][sy]);
166         tmp.g++;
167         if(vis_f[Cantor(tmp)]!=-1){cout<<tmp.g+vis_f[Cantor(tmp)];exit(0);}
168         if(vis_b[Cantor(tmp)]==-1){
169           vis_b[Cantor(tmp)]=tmp.g;
170           b.push(tmp);
171         }
172     }
173 }
174
175 void double_BFS(){
176     int len1,len2;
177     f.push(Head);b.push(Tail);
178     vis_f[Cantor(Head)]=vis_b[Cantor(Tail)]=0;
179     while(!f.empty() || !b.empty()){
180         len1=f.size();len2=b.size();
181         if(len1>len2){
182             if(len2)expand_b();
183             expand_f();
184         }
185         else {
186             if(len1)expand_f();
187             expand_b();
188         }
189     }
190 }
191
192 int main(){
193     init();
194     double_BFS();
195     return 0;
196 }

代码量比较大,竟然有个小200....

时间: 2024-11-24 04:46:22

洛谷OJ P1379 八数码难题 解题报告的相关文章

洛谷OJ P1045 麦森数 解题报告

洛谷OJ P1045 麦森数 解题报告 by MedalPluS   题目描述 形如2P-1的素数称为麦森数,这时P一定也是个素数.但反过来不一定,即如果P是个素数,2P-1不一定也是素数.到1998年底,人们已找到了37个麦森数.最大的一个是P=3021377,它有909526位.麦森数有许多重要应用,它与完全数密切相关. 任务:从文件中输入P(1000<P<3100000),计算2P-1的位数和最后500位数字(用十进制高精度数表示)   输入描述   文件中只包含一个整数P(1000&l

洛谷OJ P1032 字串变换 解题报告

洛谷OJ P1032 字串变换 解题报告 by MedalPluS   [题目描述] 已知有两个字串 A$, B$ 及一组字串变换的规则(至多6个规则): A1$ -> B1$ A2$ -> B2$ 规则的含义为:在 A$中的子串 A1$ 可以变换为 B1$.A2$ 可以变换为 B2$ …. 例如:A$='abcd' B$='xyz' 变换规则为: ‘abc’->‘xu’ ‘ud’->‘y’ ‘y’->‘yz’ 则此时,A$ 可以经过一系列的变换变为 B$,其变换的过程为:

洛谷OJ P1126 机器人搬重物 解题报告

洛谷OJ P1126 机器人搬重物 解题报告 by MedalPluS [题目描述]    机器人移动学会(RMI)现在正尝试用机器人搬运物品.机器人的形状是一个直径1.6米的球.在试验阶段,机器人被用于在一个储藏室中搬运货物.储藏室是一个N*M的网格,有些格子为不可移动的障碍.机器人的中心总是在格点上,当然,机器人必须在最短的时间内把物品搬运到指定的地方.机器人接受的指令有:向前移动1步(Creep):向前移动2步(Walk):向前移动3步(Run):向左转(Left):向右转(Right).

洛谷 P1379 八数码难题

P1379 八数码难题 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变. 输入输出格式 输入格式: 输入初始状态,一行九个数字,空格用0表示 输出格式: 只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊

洛谷 P1379 八数码难题 Label:判重&amp;&amp;bfs

特别声明:紫书上抄来的代码,详见P198 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变. 输入输出格式 输入格式: 输入初试状态,一行九个数字,空格用0表示 输出格式: 只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数

洛谷P1379 八数码难题

题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变. 输入输出格式 输入格式: 输入初始状态,一行九个数字,空格用0表示 输出格式: 只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)

P1379 八数码难题

题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变. 输入输出格式 输入格式: 输入初试状态,一行九个数字,空格用0表示 输出格式: 只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)

luogu#P1379 八数码难题

题意: 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变. 解法: A* #include<iostream> #include<queue> #include<map> #define END 123804765 using

[LGOJ]P1379八数码难题[双向广搜].cpp

已知初始状态和目标状态时可用双向搜索 有种神仙代码实际上不对, 没有判重, 拓展了很多重复的状态节点 但还是能算出答案, 用小样例验证时还看不出来, 一交全TLE 所以千万要记得判重 详见代码, 自认为码风清奇 #include<cstdio> #include<map> #include<queue> #define re register using namespace std; int str,endd=123804765;//矩阵只有3*3大小, 用int存方便