骑士精神 (迭代加深)

Description

像骑士的忠贞 
不畏惧邪恶的眼神 
这过程一直放在我心底就像 
挡在你胸前的盔甲 
保护着我让我心疼 
骑士们发挥出你们的精神 
就这样强悍的骑士撑到最后 
骄傲的公主的要回家整装再出发。 


这是蔡依林的歌曲骑士精神,听歌归听歌,题还是得做。题目要求在一个5×5的棋盘上放12个白色的骑士和12个黑色的骑士, 且有一个空位。在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上。 给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘: 为了体现出骑士精神,他们必须以最少的步数完成任务。

Input

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

Output

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

Sample Input

2
10110
01*11
10111
01001
00000

01011
110*1
01110
01010
00100

Sample Output

7
-1

思路:

其实这题我以前是不会的,以前没有系统的学习过相关知识,所以大概只知道是搜索方面的题,具体怎么做不知道。

后来才知道是迭代加搜索。

“迭代加深搜索(Iterative Deepening Depth-First Search, IDDFS)经常用于理论上解答树深度上没有上界的问题,这类问题通常要求出满足某些条件时的解即可。”


进一步理解之前,我们先比较下深搜,广搜,和迭代加深搜。

深度优先搜索(DFS):

算法总是能尽可能快的抵达搜索树的底层。

n 皇后问题的空间复杂度为 O(n)如果搜索树有 d 层,每个节点有 c 个子节点,时间复杂度与c^d成正比。


广度优先搜索(BFS):

每次都先将搜索树某一层的所有节点全部访问完毕后再访问下一层。

广度优先搜索的空间复杂取决于每层的节点数。如果搜索树有 k 层,每个节点有 c 个子节点,那么最后将可能有 c^k 个数据被存入队列 


迭代加深搜索(ID):

广度优先搜索可以用迭代加深搜索代替。迭代加深搜索实质是限定下界的深度优先搜索,即首先允许深度优先搜索搜索 k 层搜索树,若没有发现可行解,再将 k+1 后再进行一次以上步骤,直到搜索到可行解。这个“模仿广度优先搜索”搜索法比起广搜是牺牲了时间,但节约了空间。


具体怎么操作呢?

迭代加深搜索是通过限制每次dfs的最大深度进行的搜索。令maxd表示最大的搜索深度,那么dfs就只能在0~maxd之间来进行,如果在这个范围内找到了解,就退出大循环,否则maxd++,扩大搜索范围。但可想而知,倘若没有高效及时的退出无解的情况,那么时间上的开销也是会比较大的。这时就需要进行“剪枝”操作,及时地判断此时能否找到解。对于迭代加深搜索,经常通过设计合适的“乐观估价函数”来判断能否剪枝。设当前搜索的深度是cur,乐观估价函数是h(),那么当cur+h()>maxd时就需要剪枝。

顺便提以下乐观估计函数:简单的说就是从当前深度到找到最终的解“至少”还需要多少步,或者距离找到最终的解还需要扩展多少层。如果超出了当前限制的深度maxd,说明当前限制的最大深度下是不可能找到解的,直接退出。


暂时理解那么多,再看题目就很明显了,尝试用迭代加深搜的办法去解。
#include <cstdio>  
#include <algorithm>  
using namespace std;  
int find1 = 0;  
int a[5][5];  
int target[5][5]= {   
    {1,1,1,1,1},   
    {0,1,1,1,1},   
    {0,0,2,1,1},   
    {0,0,0,0,1},   
    {0,0,0,0,0}   
};   
int dx[]={1, 1, -1, -1, 2, 2, -2, -2};  
int dy[]={2, -2, 2, -2, 1, -1, 1, -1};  
int dis() {  
    int v = 0;  
    for(int i = 0; i < 5; i++)  
        for(int j = 0; j < 5; j++) {  
            if(a[i][j] != target[i][j])  
                v++;  
        }  
        return v;  
}  
void dfs(int x, int y, int s, int k) {    
    if(s == k) {   
        if(dis() == 0) {   
            find1 = 1;   
        }   
        return;   
    }   
    for(int i = 0; i < 8 ; i++) {   
        int nx = x + dx[i];   
        int ny = y + dy[i];   
        if(nx < 0 || nx > 4 || ny < 0 || ny > 4) continue;   
        swap(a[x][y], a[nx][ny]);   
        if(s + dis() <= k ) {  //用乐观估价函数判断能否剪枝. 
            dfs(nx, ny, s + 1, k);   
        }  
        swap(a[x][y], a[nx][ny]);   
    }   
}  
int main() {  
	int n, sx, sy, k; 
    char ch; 
    scanf("%d",&n);  
    while(n--){   
        for(int i = 0; i < 5; i++)   
            for(int j = 0; j < 5; j++) {   
                scanf(" %c", &ch);  
                if(ch  == '*') {   
                    a[i][j] = 2;   
                    sx = i;   
                    sy = j;   
                }   
                else 
                    a[i][j] = ch - '0';   
            }   
            for(k = 0; k <= 10; k++) {//迭代加深搜索 判断能不能在10步内搞定     
                dfs(sx, sy, 0, k);   
                if(find1) break;   
            }   
            printf("%d\n",k > 10 ? -1 : k);   
            find1 = 0;   
            getchar();  
    }  
    return 0;  
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值