【洛谷2324】[SCOI2005]騎士精神 IDA*


[SCOI2005]騎士精神

描述

 在一個\(5×5\)的棋盤上有\(12\)個白色的騎士和\(12\)個黑色的騎士, 且有一個空位。在任何時候一個騎士都能按照騎
士的走法(它可以走到和它橫坐標相差為\(1\),縱坐標相差為\(2\)或者橫坐標相差為\(2\),縱坐標相差為\(1\)的格子)移動到空
位上。 給定一個初始的棋盤,怎樣才能經過移動變成如下目標棋盤: 為了體現出騎士精神,他們必須以最少的步
數完成任務。

img

輸入

第一行有一個正整數\(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

題解

題意:給你一個初始棋盤,要求用最少的步數移動馬達到如上圖的目標狀態(要求棋盤中的馬只能走“日”)。

咱們先拋開\(IDA^*\),先如何優化爆搜;

這里的馬和象棋里的馬走法相同,但題目中要求讓馬走,但是要是馬的話,搜索分支比較多,所以我們要考慮讓空格走(很顯然吧)。

下面步入正題:

\(IDA^*\)就是帶有迭代加深和估價函數優化的搜索。

可能某些人對以上兩個名詞很陌生,下面一些前置知識可能會帶你透徹一下。

前置知識1:迭代加深
定義:

每次限定一個\(maxdep\)最大深度,使搜索樹的深度不超過\(maxdep\)

	for(R int maxdep=1;maxdep<=題目中給的最大步數;maxdep++){
		dfs(0,maxdep);//0為出入函數中當前步數,maxdep為傳入的最大深度。
		if(success)break;//如果搜索成功則會在dfs函數中將success賦值為1。
	}
使用范圍:

1.在有一定的限制條件時使用(例如本題中“如果能在\(15\)步以內(包括\(15\)步)到達目標狀態,則輸出步數,否則輸出\(-1\)。“)。

2.題目中說輸出所以解中的任何一組解。

為什么能夠降低時間復雜度:

我們可能會在一個沒有解(或解很深的地方無限遞歸然而題目中要求輸出任何的一組解),所以我們限制一個深度,讓它去遍歷更多的分支,去更廣泛地求解,(其實和\(BFS\)有異曲同工之妙)。

前置知識2:估價函數
定義:

\(f(n)=g(n)+h(n)\)

其中\(f(n)\)是節點的估價函數,\(g(n)\)是現在的實際步數,\(h(n)\)是對未來步數的最完美估價(“完美”的意思是可能你現實不可能實現,但你還要拿最優的步數去把\(h(n)\)算出來,可能不太好口胡,可以參考下面的實例)。

應用:
    void dfs(int dep,int maxdep){
        if(evaluate()+dep>maxdep)return;
        //evaluate函數為對未來估價的函數,若未來估價加實際步數>迭代加深的深度則return。
        if(!evaluate){
            success=1;
            printf("%d\n",dep);
            return;
        }
        ......
    }
前置知識3:\(A^*\)\(IDA^*\)的區別

\(A^*\)是用於對\(BFS\)的優化;

\(IDA^*\)是對結合迭代加深的\(DFS\) 的優化。

本質上只是在\(BFS\)\(DFS\)上加上了一個估價函數。

何時使用因題而定:

\(A^*\)[SCOI2007]k短路);\(IDA^*\)[SCOI2005]騎士精神UVA11212 Editing a Book 就是上面的兩道題)。

前置知識畢!!!

現在就是要想一個比較好的估價函數(若估價函數不好的話,優化效率就並不高,例如若估價函數一直為0,那就是爆搜)。

我們可以想一下,每次空白格子和黑白棋子交換,最優的情況就是每次都把黑白棋子移動到目標格子。

那么你的估價函數就出來了:

    const int goal[7][7]={
        {0,0,0,0,0,0},
        {0,1,1,1,1,1},
        {0,0,1,1,1,1},
        {0,0,0,2,1,1},
        {0,0,0,0,0,1},
        {0,0,0,0,0,0}
    };    
    inline int evaluate(){
        R int cnt=0;
        for(R int i=1;i<=5;i++)
            for(R int j=1;j<=5;j++)
                if(mp[i][j]!=goal[i][j])cnt++;
        return cnt;
    }

下面就是爆搜了:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cctype>
#define ll long long
#define R register
using namespace std;
template<typename T>inline void read(T &a){
    char c=getchar();T x=0,f=1;
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    a=f*x;
}
int n,m,t,mp[7][7],stx,sty,success;
char ch;
const int dx[]={0,1,1,-1,-1,2,2,-2,-2};
const int dy[]={0,2,-2,2,-2,1,-1,1,-1};
const int goal[7][7]={
    {0,0,0,0,0,0},
    {0,1,1,1,1,1},
    {0,0,1,1,1,1},
    {0,0,0,2,1,1},
    {0,0,0,0,0,1},
    {0,0,0,0,0,0}
};
inline int evaluate(){
    R int cnt=0;
    for(R int i=1;i<=5;i++)
        for(R int j=1;j<=5;j++)
            if(mp[i][j]!=goal[i][j])cnt++;
    return cnt;
}
inline int safe(R int x,R int y){
    if(x<1||x>5||y<1||y>5)return 0;
    return 1;
}
inline void A_star(R int dep,R int x,R int y,R int maxdep){
    if(dep==maxdep){
        if(!evaluate())success=1;
        return;
    }
    for(R int i=1;i<=8;i++){
        R int xx=x+dx[i];
        R int yy=y+dy[i];
        if(!safe(xx,yy))continue;
        swap(mp[x][y],mp[xx][yy]);
        int eva=evaluate();
        if(eva+dep<=maxdep)
            A_star(dep+1,xx,yy,maxdep);
        swap(mp[x][y],mp[xx][yy]);//回溯
    }
}
int main(){
    read(t);
    while(t--){
        success=0;
        for(R int i=1;i<=5;i++){
            for(R int j=1;j<=5;j++){
                cin>>ch;
                if(ch=='*')mp[i][j]=2,stx=i,sty=j;//記錄起點即為空白格子
                else mp[i][j]=ch-'0';
            }
        }
        if(!evaluate()){printf("0\n");continue;}
        for(R int maxdep=1;maxdep<=15;maxdep++){
            A_star(0,stx,sty,maxdep);
            if(success){printf("%d\n",maxdep);goto ZAGER;}
        }
        printf("-1\n");
        ZAGER:;
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM