注:本解题报告里的有些思路不是由本人想出,是通过百度搜出来的。所以在此膜拜一下做出来的大牛们。
(个人认为) 这 是BFS的进阶题,以前做过的BFS都是按照操作一步步来,最终找出目标状态。这题巧妙的地方在于可以无视一些操作和把操作分类。比如说:光标左移是可以 无视的。因为光标移动是连续的,而且开始光标在最左边。也就是说,如果光标在第3个数的话,那么它一定经过0,1,2这几个位置。所以这时如果再考虑光标 左移是没有意义的。另外就是操作分类。所谓操作分类就是把操作分为两类(这两类分别对状态做出不同的影响):一类移动光标,另一类就是直接把数加到目标状 态,要注意的地方是,只有光标经过的地方才能进行加减。
这样一来,记录光标到过的位置就很重要了。一般能想到的是2^6种状态,但仔细考虑之后发现其实只有10种状态,分别是
0:0
1:0, 1
2:0, 1, 2
3:0, 1, 2, 3
4:0, 1, 2, 3, 4
5:0, 5
6:0, 1, 5
7:0, 1, 2, 5
8:0, 1, 2, 3, 5
9:0, 1, 2, 3, 4, 5
第6种状态之后会出现5这个位置,是因为swap1操作。
后记:
本题可以改进的地方有几个:
1.存储状态可以进一步压缩,据说只要10*6!就可以把所有状态存完
2.据说A*算法,但是我还没想到有效的评价函数(T_T)
3.看discuss,发现有人用双向BFS写出来了,看来我也要纠结一下如何用双向BFS写出来才行……
================传说中华丽的分割线=====================
#pragma warning(disable:4786)
#include <iostream>
#include <queue>
#include <cmath>
using namespace std;
#define N 1000000
int start, end, v[ 6 ] ;
bool s[ N] [ 10 ] ;
struct node
{
int state;//当前状态
int step;//移动步数
int visited;//光标到达过的位置
/*
0:0
1:0, 1
2:0, 1, 2
3:0, 1, 2, 3
4:0, 1, 2, 3, 4
5:0, 5
6:0, 1, 5
7:0, 1, 2, 5
8:0, 1, 2, 3, 5
9:0, 1, 2, 3, 4, 5
*/
} ;
void toArray( int n, int *v)
{
for ( int i = 5 ; i >= 0 ; i--)
{
v[ i] = n % 10 ;
n /= 10 ;
}
}
int toNum( int *v)
{
int ans, i;
for ( i = 0 , ans = 0 ; i < 6 ; i++)
ans = ans * 10 + v[ i] ;
return ans;
}
//pos1:0 or 5, pos2:当前光标位置
int swapNum( int pos1, int pos2, int *v)
{
int tmp, ans;
//swap pos1 and pos2
tmp = v[ pos1] ;
v[ pos1] = v[ pos2] ;
v[ pos2] = tmp;
ans = toNum( v) ;
//把数组恢复原样
tmp = v[ pos1] ;
v[ pos1] = v[ pos2] ;
v[ pos2] = tmp;
return ans;
}
int check( node& in)
{
int tmp, res, i, a[ 6 ] ;
toArray( in.state , a) ;
tmp = in.visited ;
if ( tmp <= 4 )
{
for ( i = tmp + 1 ; i < 6 ; i++)
if ( a[ i] != v[ i] ) return -1 ;
for ( res = 0 , i = 0 ; i <= tmp; i++)
res += abs ( a[ i] - v[ i] ) ;
}
else
{
tmp -= 5 ;
for ( i = tmp + 1 ; i < 5 ; i++)
if ( a[ i] != v[ i] ) return -1 ;
for ( res = 0 , i = 0 ; i <= tmp; i++)
res += abs ( a[ i] - v[ i] ) ;
res += abs ( a[ 5 ] - v[ 5 ] ) ;
}
return res;
}
int BFS( )
{
queue<node> q;
node p;
int ori, visited, tmp, a[ 6 ] , res, minimum = N;
memset ( s, false , sizeof ( s) ) ;
p.state = start;
p.step = 0 ;
p.visited = 0 ;
q.push ( p) ;
s[ start] [ 0 ] = true ;
while ( !q.empty ( ) )
{
p = q.front ( ) ;
q.pop ( ) ;
ori = p.state ;
visited = p.visited ;
res = check( p) ;
if ( res != -1 )
{
if ( minimum > res + p.step ) minimum = p.step + res;
}
p.step ++;
if ( p.step >= minimum) continue ;//剪枝
toArray( p.state , a) ;
//swap0
if ( p.visited > 0 )
{
if ( p.visited >= 5 ) tmp = swapNum( 0 , p.visited - 5 , a) ;
else tmp = swapNum( 0 , p.visited , a) ;
if ( !s[ tmp] [ p.visited ] )
{
p.state = tmp;
q.push ( p) ;
s[ tmp] [ p.visited ] = true ;
}
}
//swap1
if ( p.visited < 5 )
{
tmp = swapNum( 5 , p.visited , a) ;
p.visited += 5 ;
}
else tmp = swapNum( 5 , p.visited - 5 , a) ;
if ( !s[ tmp] [ p.visited ] )
{
p.state = tmp;
q.push ( p) ;
s[ tmp] [ p.visited ] = true ;
}
//right
p.state = ori;
p.visited = visited;
if ( p.visited < 5 )
{
p.visited ++;
if ( !s[ p.state ] [ p.visited ] ) q.push ( p) ;
}
else if ( p.visited < 9 )
{
p.visited ++;
if ( !s[ p.state ] [ p.visited ] ) q.push ( p) ;
}
}
return minimum;
}
int main( )
{
while ( cin >> start >> end)
{
toArray( end, v) ;
cout << BFS( ) << endl;
}
return 0 ;
}