csdn同步
原題鏈結
簡要題意:
給定乙個初始棋盤,每次乙個馬可以跳到空位(不考慮蹩腿問題)。求到達目標棋盤的最小步數。
本題其實是 八數碼難題 的乙個強化版,可以去看看 p1379 八數碼難題 題解.
首先本題肯定是搜尋。
咦?\(\texttt\) 的效率不是嚴格不優於 \(\texttt\) 的麼?為什麼還要用它呢?
嗯,只要我們加上一點點優化,\(\texttt\) 就不叫 \(\texttt\) 了,它換了乙個名字,叫做 \(\text\) 演算法;如果你再優化億點點,就可以再公升級為 \(\text\) 演算法!
那麼,這些優化是什麼?為什麼只有 \(\texttt\) 才能用呢?而 \(\text\) 都是什麼呢?
我們來一層層解決這個問題。
首先,我們畫出乙個搜尋狀態圖(大概)。
紅色是起點狀態,綠色是目標狀態。在兩紅色輪廓線中的是我們要搜尋的所有狀態。
可是,你會發現有一些東西完全不用搜尋。就比方說,如果跳了一步之後,反而比原來不跳更差了,那麼這步就不用做了。
對,這就是乙個有力的剪枝,這樣的搜尋方式被稱為 \(\text\).
那麼,如果確定當前狀態的優劣性呢?
這時我們引進了估價函式 \(h\)的概念。
\(h\) 它的主要作用是估計當前狀態到目標狀態的步數。(與實際答案相差很大,但可作為參考)其特點在於,\(h\)返回的是最小可能的步數,不可能從當前狀態用 \(步完成問題。
所以,如果當前狀態為 \(x\),從起點走了 \(g_x\) 步到達 \(x\),然後其估價函式 咕值函式 為 \(h_x\),\(f_x\) 為起點的估價函式。此時我們力求滿足:
\[f_x = g_x + h_x
\]此時搜尋的效率就取決於 \(h\) 到底怎麼寫。如何快速估計最小步數?(小學數學估算題)
比方說,當前狀態(左)與目標狀態(右)如下:
1 1 1 1 1 -> 0 0 0 0 0
0 1 1 1 1 -> 1 0 0 0 0
0 0 * 1 1 -> 1 1 * 0 0
0 0 0 0 1 -> 1 1 1 1 0
0 0 0 0 0 -> 1 1 1 1 1
我們草率地估計,最少需要 \(24\) 步。為什麼呢?
因為這兩個狀態有 \(24\) 個位置不同(除了空位都不同),那麼如果每一步都能讓乙個位置正確地歸位(即達到目標狀態),這樣也需要 \(24\) 步才能讓每個位置歸位,這是我們能估算的較準確值了。儘管與答案相差較大,但這是我們能估算的最大值了。
那麼,如果當前走了一步之後有 \(25\) 個不同(就比方說你把 \(1,2\) 移到 \(3,3\) 空位上),那麼這種搜尋顯然無效,只會變劣,直接停止。
所以你發現這個圖如果用剪枝的話一步也走不了了(因為無論怎麼走都是 \(25\) 個不同),返回無解。這比你大力 \(\text\) 剪枝要快得多吧!
下面我們引進迭代加深搜尋的概念。
什麼叫做迭代加深呢?
比方說,現在你可能搜尋到的最大深度是 \(10^9\),但答案只有 \(\leq 10\) 步,而寬度隨著深度的增大也**性增長. 此時,你無法用 \(\text\) 和 \(\text\) 任何一種解決。這時需要迭代加深搜尋。什麼意思呢?
大概步驟是:
列舉當前深度(步數),進入 \(\text\).
如果當前搜尋超過深度直接結束;否則向下乙個狀態搜尋。
有解則結束返回當前深度;否則列舉下乙個深度。
深度列舉到一定範圍發現無解則返回無解。
也就是列舉搜尋深度進行搜尋,這樣子一步步搜尋,可以說是基於 \(\text\) 與 \(\text\) 之間的一種演算法吧。
下面我們要將這兩個演算法結合,迭代加深搜尋與 \(\text\) 結合就變成了 \(\text\). 步驟:
列舉最大步數,進入 \(\text\).
如果當前步數 \(+ f\) 值超過最大步數則結束;否則向下乙個狀態搜尋。
有解則結束返回當前步數;否則列舉下乙個步數。
步數列舉到一定範圍發現無解則返回無解。
其實就是在 \(\text\) 基礎上限定深度,在迭代加深搜尋上加乙個有力剪枝。
那麼,本題中這個一定範圍是多少呢?其實你設成 \(15\) 就夠了,因為題目說了:
如果能在 \(15\) 步以內(包括 \(15\) 步)到達目標狀態,則輸出步數,否則輸出 \(-1\)。如果最大步數超過 \(15\) 就不用再做了。
時間複雜度:\(o(wys)\).(很優卻難以分析的複雜度一般這樣稱呼)
實際得分:\(100pts\).
#pragma gcc optimize(2)
#includeusing namespace std;
inline int read()
int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
int t,ans=1e9;
char end[6][6]=,
, ,, ,
} ; //結束狀態
char a[6][6];
bool ok=0;
const int dx[8]=;
const int dy[8]=;
inline int g() //不同的個數 , 即估價函式
inline void dfs(int dep,int x,int y,int bs) //如果完全相同則結束搜尋
if(dep==bs) for(int i=0;i<=8;i++)
}int main() if(!g()) //不同的是 0 個 , 即起點與終點相同
for(int bs=1;bs<=15;bs++)
} puts("-1"); fin:;
} return 0;
}
文末:還有一種非常卑鄙的優化,因為如果最後答案是 \(-1\) 時間較大(當然可以通過測試)而其餘答案的耗時較小,採取 「只要程式執行超過某時間就返回 \(-1\) 的方式」,非常不嚴謹但很實用,可以大大提高效率。建議考試不要用,防止極限(如正好 \(15\) 步)資料。 P2324 SCOI2005 騎士精神
ida 演算法 12個白騎士和12個黑騎士在乙個5 5的棋盤上走來走去,求最後走成目標局面的最小步數。看不懂請走傳送門 ida 就是帶有迭代加深和估價函式優化的搜尋。估價函式 對未來搜尋深度的最優估計 通常是達不到的 對於估價函式g,要求g 現實步數 越接近越好,因為越接近能剪的枝就越多 當當前步數...
P2324 SCOI2005 騎士精神
輸入格式 第一行有乙個正整數t t 10 表示一共有n組資料。接下來有t個5 5的矩陣,0表示白色騎士,1表示黑色騎士,表示空位。兩組資料之間沒有空行。輸出格式 對於每組資料都輸出一行。如果能在15步以內 包括15步 到達目標狀態,則輸出步數,否則輸出 1。輸入樣例 1 複製 2 10110 01 ...
洛谷 P2324 SCOI2005 騎士精神
ida 練習題 1 ida iddfs a 1.1 iddfs用於單層 無限 情況但是解層數很少,這時可以限制dfs層數,id為每次的層數限制 1.2 a 具有估價函式,用於bfs快速求解 ida 就是將估價函式用於層數限制的dfs中,如果具有層數的dfs仍然無法滿足時間限制,那麼可以考慮題目性質進...