1. 程式人生 > >洛谷 P2324 [SCOI2005]騎士精神 題解

洛谷 P2324 [SCOI2005]騎士精神 題解

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]騎士精神 題解