UVA439 騎士的移動

2022-03-16 19:58:03 字數 3244 閱讀 3758

之所以這道題我要寫題解,是因為解題的過程中我採用了多種方法(不嚴謹的說,基本寫完了搜尋裡的所有技巧)——bfs,ida* ,a*,雙向dfs。

這個過程很值得品味參考,於我來說也是一次不可多得的學習。

這道題的bfs思路是比較顯然的,**實現上也不算特別難。

#include#define debug  printf("ok\n");

int f[10][10],dx[9]=,dy[9]=;

char op[5],ed[5];

struct node s,t;

namespace walkerv

void solve() ,t=(node);

std::queueq;

q.push(s),f[s.x][s.y]=0;

while(q.size()>0)

for(int i=1;i<=8;i++) );}}

} return;

} void print()

}int main()

return 0;

}

然後便是ida*。

如果你問我為什麼不先寫a*,因為這份**的重點是迭代加深,也就是id(iterative deepening)。但單打乙份迭代加深的dfs意義不大,所以就選擇用ida*。而且從各種角度來說,ida*都比a*要優秀一些。

但是這份**應該說是幾份**我調得最痛苦的乙份(大概前前後後修了三四個小時左右),最後的關鍵錯誤還是落在了估價函式上。

有必要說一下估價函式的設計:因為是馬在棋盤上的移動,所以考慮曼哈頓距離。我們知道馬每次移動的曼和頓距離為$3$,所以樂觀估計應當是當前位置到終點的曼哈頓距離除以$3$並向上取整。

#includeint max_dep;

int f[10][10],dx[9]=,dy[9]=;

char op[5],ed[5];

struct node s,t;

namespace walkerv

double estimate_function(int x1,int y1,int x2,int y2)

bool iddfs(int x,int y,int dep)

else

} //printf("x:%d y:%d g(x):%d f(x):%.1f\n",x,y,dep,estimate_function(x,y,t.x,t.y));

for(int i=1;i<=8;i++)

if(iddfs(x+dx[i],y+dy[i],dep+1))

}} return false;

} void solve() ,t=(node);

while(!iddfs(s.x,s.y,0))

return;

} void print()

}int main()

return 0;

}

在有了bfs和ida*的基礎上,a*就顯得非常容易了。

估價函式的設計與ida*是一致的。

#includeint f[10][10],dx[9]=,dy[9]=;

char op[5],ed[5];

struct node

}s,t;

namespace walkerv

double estimate_function(int x,int y)

void solve() ,t=(node);

s.est=estimate_function(s.x,s.y);

std::priority_queueq;

q.push(s),f[s.x][s.y]=0;

while(q.size())

for(int i=1;i<=8;i++) );}}

} return;

} void print()

}int main()

return 0;

}

對於這題,由於我們已經知道搜尋的初態和末態,所以既可以從初態搜到末態,也可以從末態搜到初態。

在此基礎上,雙向bfs的思路也就呼之欲出了——從初態和末態同時往中間搜尋(具體實現應當是正反各搜一輪)。

由於我們使用的是bfs,所以在輪流搜尋的時候但凡有乙個狀態被兩邊都搜尋到了(也就是說第乙個被兩邊都搜尋到的狀態),那麼這個狀態到初態和末態的路徑和就是答案(正確性顯然)。

此外,在**中需要注意的有如下兩點:

說句閒話,其實中間兩個佇列的實現那裡,如果用typedef寫會更好看一些,但是可讀性會大大降低(逃

#includeint ans;

int f1[10][10],f2[10][10],dx[9]=,dy[9]=;

char op[5],ed[5];

struct node s,t;

namespace walkerv

void solve() ,t=(node);

if(s.x==t.x&&s.y==t.y)

std::queueq1,q2;

q1.push(s),f1[s.x][s.y]=0;

q2.push(t),f2[t.x][t.y]=1;

while(q1.size()||q2.size())

else

for(int i=1;i<=8;i++)

f1[k.x+dx[i]][k.y+dy[i]]=f1[k.x][k.y]+1;

q1.push((node));

break;

case false:

if(f1[k.x+dx[i]][k.y+dy[i]])

f2[k.x+dx[i]][k.y+dy[i]]=f2[k.x][k.y]+1;

q2.push((node));

break;

} }}

} return;

} void print()

}int main()

return 0;

}

這次這道題碼下來,還算是收穫頗豐。誰會想到,乙個學了兩年多oi的人,直到這道題才算是摸透了上面四種演算法(包括bfs)。

此外,在寫**的時候,感覺到(id)a*是真正優秀的演算法。這不僅體現在它的執行時間上,而是說這種演算法是最貼近人類行為的。作為人類,我們在解決問題中面臨多種方式時,一定會略作估計並選取最優的方式進行嘗試。可能這也是啟發式演算法被廣泛運用到人工智慧裡的原因,畢竟我們人類定義的人工智慧就是與人類智慧型所相似的機器。

UVA439深搜和寬搜區別

這題其實就是乙個變種的寬搜,很簡單,可是後來發現自己把寬搜和深搜給記反了,這題就一直寫的是深搜,但是顯然寬搜的題用深搜做事不對的,也學可以,反正本弱不會,但是前面幾個深搜的題目,我都是用寬搜 做的,事實證明深搜的題目是可以用寬搜來做的,而深搜和寬搜的最主要區別就是深蒐用棧來實現,而寬蒐用 佇列來實現...

A 移動的騎士

time limit 1000 1000ms c others memory limit 65536 65536kb c others problem description somurolov先生是乙個西洋棋高手,他聲稱在棋盤上將騎士棋子從一點移動到另外一點,沒有人比他快,你敢挑戰他嗎?你的任務是...

白騎士的移動 搜尋

小s第一次接觸西洋棋。他發現西洋棋中的knight棋子的移動方式和中國象棋中的馬類似,移動方式如圖所示。於是小s在棋盤上隨意擺上了一些棋子,其中包括一枚白騎士 一枚黑皇后 若干黑戰車和若干黑主教。小s想知道,如何能在避開黑戰車和黑主教的攻擊範圍的前提下,花費更少的步數吃掉黑皇后。輸入格式 輸入僅包含...