P2324 [SCOI2005]骑士精神(IDA*)

题目描述

P2324 [SCOI2005]骑士精神(IDA*)

输入输出格式

输入格式:

 

第一行有一个正整数T(T<=10),表示一共有N组数据。接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。两组数据之间没有空行。

 

输出格式:

 

对于每组数据都输出一行。如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。

 

输入输出样例

输入样例#1: 复制

2
10110
01*11
10111
01001
00000
01011
110*1
01110
01010
00100

输出样例#1: 复制

7
-1

说明

P2324 [SCOI2005]骑士精神(IDA*)

解题思路

 骑士移动实质就是空格移动,因为空格只有一个,所以直接搜空格的移动。下面是剪枝操作。

1.题目规定了最多只能走15步,所以在搜索的过程中,我们要设定一个步数的上限。

2.估算至少还需要多少步才能实现目标,如果当前步数+最少还需要步数 > 步数上限15,那么直接返回,没有继续搜索的必要了。至于如果估计,对于一个棋盘来说,除了最后一步能让空格和一个棋子同时归位,其他步最多只能让一个棋子归位。所以比较当前棋盘和目标棋盘,没归位的数量-1就是最少还需要的步数。

3.一直记录已经搜到过的当前最少步骤c,如果当前步骤大于等于c,则返回。

4.当我们从位置(x1,y1)跳到(x2,y2)的时候,我们不能在下一步又从(x2,y2)跳回(x1,y1),这样就死循环了。所以我们在写坐标转移的数组的时候,应该让相反的操作对称起来,然后向下传此步的下标,就能根据下标简单的判断不能跑哪一步了。

代码如下

#include <iostream>
#define max_step 16
using namespace std;
string obj[] = {"11111", "01111", "00*11", "00001", "00000"};
string str[5];
int eva()
{
    int cnt = 0;
    for(int i = 0; i < 5; i ++){
        for(int j = 0; j < 5; j ++){
            if(obj[i][j] != str[i][j])
                cnt ++;
        }
    }
    return cnt;
}
int ans;
int dx[] = {1, 1, 2, 2, -2, -2, -1, -1};  //
int dy[] = {2, -2, 1, -1, 1, -1, 2, -2};
void dfs(int x, int y, int step, int k)
{
    if(step > ans)
        return;
    int c = eva();
    if(step + c > max_step || step > max_step - 1)
        return;	
    if(!c){
        ans = step;
        return;
    }			
    for(int i = 0; i < 8; i ++){
        int xx = x + dx[i];
        int yy = y + dy[i];
        if(xx >= 0 && xx < 5 && yy >=0 && yy < 5 && i + k != 7){
            swap(str[xx][yy], str[x][y]);
            dfs(xx, yy, step + 1, i);
            swap(str[xx][yy], str[x][y]);
        }
    }
        
}
int main()
{
    int t;
    cin >> t;
    while(t --){
        for(int i = 0; i < 5; i ++)
            cin >> str[i];
        ans = 16;
        for(int i = 0; i < 5; i ++){
            for(int j = 0; j < 5; j ++){
                if(str[i][j] == '*'){
                    dfs(i, j, 0, -1);
                    break;
                }
            }
        }
        if(ans == 16)
            cout << -1 << endl;
        else 
            cout << ans << endl;
    }
    return 0;
}