洛谷 P2324 [SCOI2005]騎士精神 題解
阿新 • • 發佈:2019-03-02
problem -- upload else ext 描述 分享圖片 實現 print
題目傳送門
題目
題目描述
輸入輸出格式
輸入格式:
第一行有一個正整數T(T<=10),表示一共有N組數據。接下來有T個5×5的矩陣,0表示白色騎士,1表示黑色騎士,*表示空位。兩組數據之間沒有空行。
輸出格式:
對於每組數據都輸出一行。如果能在15步以內(包括15步)到達目標狀態,則輸出步數,否則輸出-1。
輸入輸出樣例
輸入樣例:
2
10110
01*11
10111
01001
00000
01011
110*1
01110
01010
00100
輸出樣例: 7
-1
說明
題目大意
有一個棋盤,上面有一些黑馬和白馬,問最少多少步能到達目標狀態
思路
一道有點難(也許只有我這麽認為)的搜索題目
題目說只要求15步之內的最小步數,超出了就不用算
可以從1到15枚舉(或二分)答案,用dfs判斷答案是否可行
在這題中,考慮空格的位置來搜索比較方便(畢竟只有一個空格)
考慮剪枝
1: 在搜索時記錄與目標狀態不同的不同的方格個數,因為除了最後一步可以一步將兩個不正確的格子變為正確,其他時候每一步都只能將一個格子更正
所以如果 當前已經走的步數 + 狀態不對的格子數 - 1 > 限定的答案 直接return (註意:這裏一定要-1)
if (now + ss - 1 > maxn) return false; //剪枝 1 :如果當前預計的最少步數已經超過限定的值,停止 //由於最後一步可以一下更正兩格,所以最少步數為 now + ss - 1
2:不走回頭路,如果走回去就continue
在實現時將八個方向按照一定順序搜索,進行判斷
int dx[8] = {-2, -2, -1, 1, -1, 1, 2, 2}, dy[8] = {-1, 1, 2, 2, -2, -2, -1, 1}; //dx,dy按照一定的順序來,便於判斷是否走回去 for (int i = 0; i < 8; i++) //8個方向搜索 { if (i + last == 7) continue; //如果走第i個方向是往回走就不用搜索了(這就是為什麽dx和dy要有一定順序) }
搜索方法
空格每次回與一個白馬或黑馬交換位置:
考慮馬
情況1:如果一個馬原本在正確的位置,交換後位置錯誤,則狀態不同的格子數 + 1;
情況2:和情況1相反,一個馬從錯誤位置換到了正確位置,則狀態不同的格子數 - 1;
考慮空格
情況3:如果一個空格原本在正確的位置,交換後位置一定錯誤,則狀態不同的格子數 + 1;
情況4:和情況3相反,一個空格從錯誤位置換到了正確位置,則狀態不同的格子數 - 1;
if (ch[xx][yy] == m[xx][yy] and ch[xx][yy] != m[x][y]) tmp++; //上述情況1 else if (ch[xx][yy] != m[xx][yy] and ch[xx][yy] == m[x][y]) tmp--; //情況2 if (m[x][y] == ‘*‘) tmp++; //情況3 else if (m[xx][yy] == ‘*‘) tmp--; //情況4
完整代碼
#include <bits/stdc++.h> using namespace std; int t, sx, sy, s, maxn, ans; int dx[8] = {-2, -2, -1, 1, -1, 1, 2, 2}, dy[8] = {-1, 1, 2, 2, -2, -2, -1, 1}; //dx,dy按照一定的順序來,便於判斷是否走回去 char ch[5][5]; char m[5][5]={ {‘1‘, ‘1‘, ‘1‘, ‘1‘, ‘1‘}, {‘0‘, ‘1‘, ‘1‘, ‘1‘, ‘1‘}, {‘0‘, ‘0‘, ‘*‘, ‘1‘, ‘1‘}, {‘0‘, ‘0‘, ‘0‘, ‘0‘, ‘1‘}, {‘0‘, ‘0‘, ‘0‘, ‘0‘, ‘0‘}}; //打表便於對比狀態 bool ok (int x, int y, int now, int ss, int last) //x,y表示坐標,now表示當前步數,ss表示預計最少步數,last表示上一次走的方向 { if (now + ss - 1 > maxn) return false; //剪枝 :如果當前預計的最少步數已經超過限定的值,停止 //由於最後一步可以一下更正兩格,所以最少步數為 now + ss - 1 if (!ss) {ans = now; return true;} //如果當前狀態就是目標狀態,記錄答案,返回true for (int i = 0; i < 8; i++) //8個方向搜索 { int tmp = ss; if (i + last == 7) continue; //如果走第i個方向是往回走就不用搜索了(這就是為什麽dx和dy要有一定順序) int xx = x + dx[i], yy = y + dy[i]; if (xx < 0 or xx > 4 or yy < 0 or yy > 4) continue; if (ch[xx][yy] == m[xx][yy] and ch[xx][yy] != m[x][y]) tmp++; //上述情況1 else if (ch[xx][yy] != m[xx][yy] and ch[xx][yy] == m[x][y]) tmp--; //情況2 if (m[x][y] == ‘*‘) tmp++; //情況3 else if (m[xx][yy] == ‘*‘) tmp--; //情況4 swap (ch[x][y], ch[xx][yy]); bool k = ok (xx, yy, now + 1, tmp, i); if (k) return true; swap (ch[x][y], ch[xx][yy]); //回溯 } return false; } int main() { scanf ("%d", &t); while (t--) { s = 0, ans = -1; for (int i = 0; i < 5; i++) for (int j = 0; j < 5; j++) { cin >> ch[i][j]; if (ch[i][j] == ‘*‘) sx = i, sy = j; if (ch[i][j] != m[i][j]) s++; //如果和目標不同,統計加 1 } for (int i = s; i <= 15; i++){ maxn = i; if (ok (sx, sy, 0, s, 10)) break; } printf ("%d\n", ans); } return 0; }
洛谷 P2324 [SCOI2005]騎士精神 題解