標籤:華為機試 圍棋吃子 下圍棋 深度優先遍曆
題目:
圍棋中,一個棋子在棋盤上,與它直接緊鄰的空點是這個棋子的“氣”,棋子直接緊鄰的點上,如果有同色妻子存在,則它們便相互組成一個不可分割的整體,它們的“氣”也應一併計算。如果一個或一片棋子的“氣”為0,那它們將被吃掉。
1. 一個棋子的情況,如下左圖,白棋右側還有一個空點,此時白棋氣為1,不會被吃掉。當黑棋在此空點下棋後,白棋氣為0,將被吃掉。
2. 一片棋子的情況,如,左下角的白棋下面有一個空點,由於其它白棋都與之能通過直接相鄰,此時整片白棋的氣都為1,不會被吃掉。當黑棋在該空點下棋後,白棋氣為0,將被吃掉。
3. 當下棋造成雙方棋子都沒有氣時,只有對方的棋子被吃掉。如空點處,黑棋下子後,最中間的黑棋和中圈的白棋都沒有氣,但只有白棋被吃掉。除了這種情況。不允許下棋導致本方棋子沒有氣(自殺棋)。
本題用一個10x10的二維數組表示棋盤,數組中0表示空點,1表示白棋,2表示黑棋 。棋盤左下角為(0,0),右上方為(9,9),水平為x方向,豎直為y方向。
為避免走棋步數太多,提供設定棋盤的介面可以將棋盤初始化為一個殘局狀態。之後不斷進行下棋,下棋順序不定,可以連續下黑或者連續下白(圍棋允許一方放棄下子)當棋子吃掉後,返回相應的吃子情況外,還應將被吃棋子從棋盤中移除,保證棋盤狀態正確。
已耗用時間限制:無限制
記憶體限制:無限制
輸入:輸入一個10*10的整數,初始化棋盤。輸入保證初始化的殘局中不存在氣為0的子(即被吃棋子卻沒取走的異常狀態)。輸入一行或多行整數,每行3個整數,分別表示,所下棋子的x位置,y位置,棋子類型(1表示白棋,2表示黑棋)。
輸出:一個整數。含義如下:
0:本次下棋未發生吃子
2147483647下棋錯誤(該位置已有棋子,或下棋導致本方棋子無氣)
其餘正值:有白棋被吃掉,吃掉個數等於返回值
負值:有黑棋被吃掉,吃掉個數等於返回值的絕對值
解析:
當給定一個輸入時,我們首先判斷下的這個棋子是否導致自身的棋子沒氣;然後判斷下的這個棋子是否導致對方的棋子沒氣,並統計沒氣的棋子的個數;若對方沒有棋子沒氣,但自身有棋子沒氣了,則為違規輸入;否則,返回對方沒氣的棋子的個數即可。
那麼這裡關鍵問題便是如何判斷是否有棋子沒氣了。這個問題可以用典型的dfs(深度優先)的方法來解決。給定一個位置以及該位置的棋子類型,我們首先判斷該棋子上方是否為空白,若為空白,則表示與該棋子位置相連的相同棋子均有氣;若上方棋子為相同顏色的棋子,則遞迴到該位置繼續判斷;若上方位置為對方棋子,則表示該棋子上方沒有氣。在遍曆玩該棋子的上方能夠遍曆的位置之後,發現沒有找到氣,則選擇其他方向繼續遍曆。直到一個方向找到了氣,或是四個方向均沒有氣。
#include <iostream> using namespace std; int A[10][10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; bool B[10][10]; int eatCount = 0; //初始化矩陣B都為false void initFlagMatrix() { for(int i = 0; i < 10; i++) { for(int j = 0; j < 10; j++) B[i][j] = false; } } bool hasAir(int i, int j, int type) { if(A[i][j] == 0) return true; if(A[i][j] != type) return false; eatCount++; B[i][j] = true; if(i > 0 && !B[i-1][j] && hasAir(i-1, j, type)) return true; else if(i < 9 && !B[i+1][j] && hasAir(i+1, j, type)) return true; else if(j > 0 && !B[i][j-1] && hasAir(i, j-1, type)) return true; else if(j < 9 && !B[i][j+1] && hasAir(i, j+1, type)) return true; else return false; } //將與A[i][j]相連的相同類型的棋子全部吃掉 void eatChess(int i, int j, int type) { if(A[i][j] != type) return; A[i][j] = 0; //吃掉子 if(i > 0) eatChess(i-1, j, type); if(i < 9) eatChess(i+1, j, type); if(j > 0) eatChess(i, j-1, type); if(j < 9) eatChess(i, j+1, type); } //尋找整個棋盤看棋子是否有氣 bool hasAirOfType(int type, int &p, int &q) { for(int i = 0; i < 10; i++) { for(int j = 0; j < 10; j++) { if(A[i][j] != type || B[i][j]) continue; eatCount = 0; if(!hasAir(i, j, type)) { p = i, q = j; return false; } } } return true; } //當位置[i,j]放個黑白子的時候吃掉子的個數 int eatenChesscount(int i, int j, int type) { initFlagMatrix(); bool self_hasAir = hasAir(i, j, type); eatCount = 0; int p = 0, q = 0; int other_type = (type==1?2:1); initFlagMatrix(); bool other_hasAir = hasAirOfType(other_type, p, q); if(!self_hasAir && other_hasAir)//自殺 { A[i][j] = 0; return 2147483647; } if(!other_hasAir) { eatChess(p, q, other_type); if(other_type == 1) //白子正數return eatCount; else //黑子負數return 0-eatCount; } return 0; } //列印10*10矩陣的殘局狀態void printChessState() { for(int i = 0; i < 10; i++) { for(int j = 0; j < 10; j++) { cout << A[i][j] << " "; } cout << endl; } } int main() { int i, j, type; while(cin >> i >> j >> type) { if(A[i][j] != 0) { cout << "2147483647" << endl; continue; } A[i][j] = type;cout<<"當前的殘局狀態:"<<endl; printChessState(); cout << "此次吃子個數:"<< eatenChesscount(i, j, type) << endl; cout<<"吃子後殘局狀態"<<endl; printChessState(); } return 0;}
華為機試—圍棋吃子(下圍棋)判決(進階題160分:深度優先遍曆)(圖文吐血整理)