1. 程式人生 > 實用技巧 >假設圍棋棋盤是完全圖Kn,那麼最優解是什麼?

假設圍棋棋盤是完全圖Kn,那麼最優解是什麼?

猜想:

以下預設貼子=0;

n=1: 和棋(顯然)

n=2: 開局兩棄後終局,和棋。誰先動手誰死。

n=3: 如果開局黑棋走棋,白棋有保證不敗的策略。根據copycat lemma,黑棋也有保證不敗的策略。因此最優解還是指向和棋。

n=4: 沒想好

n=5以上... 誰愛研究誰研究去(

數學計算:

我發現在的棋盤上,當時不會出現打劫,也就是不會出現(除了禁全同和禁止自己填滿棋盤以外)禁止落子的情況。這樣的話,棋盤的狀態就可以用個狀態來表示(減去的是棋盤被填滿的非法狀態)。為了考慮全同,在每一個搜尋狀態下,我們都需要記錄經過的所有棋盤狀態。這樣的話,我們搜尋狀態的總數有不超過個。emm...

我實現了深度(暴)優先(力)搜尋的程式。加了一些剪枝後,跑出了n=4的結果,竟然是先手獲勝。

最後的n=5很巧啊,平局

附上程式碼:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
#define F first
#define S second
#define MP make_pair
typedef long long ll;
const int mx = 10000000;
int n, stat_n, length = 0;
int full_white = 0, full_black = 0;
int pow3[20], exist[mx], record[mx];

void init () {
	pow3[0] = 1;
	for (int i = 1; i <= n; i ++)
		pow3[i] = pow3[i - 1] * 3;
	for (int i = 0; i < n; i ++) {
		full_black += pow3[i];
		full_white += pow3[i] * 2;
	}
}

int encode (int *stat_c) {
	int msk = 0;
	for (int i = n; i >= 1; i --)
		(msk *= 3) += stat_c[i];
	return msk;
}
void decode (int msk, int *stat_c) {
	for (int i = 1; i <= n; i ++, msk /= 3)
		stat_c[i] = msk % 3;
}

int check_result (int msk) {
	int stat_c[20];
	decode (msk, stat_c);
	int w = 0, b = 0;
	for (int i = 1; i <= n; i ++, msk /= 3) {
		b += (msk % 3 == 1);
		w += (msk % 3 == 2);
	}
	return (w == b ? 0 : (w < b ? 1 : -1));
} 

string Print (int msk) {
	string s;
	for (int i = 1; i <= n; i ++, msk /= 3)
		s += (char)('0' + msk % 3);
	return s;
}

void eliminate (int &msk, int side) {
	int stat_c[20];
	decode (msk, stat_c);
	int emp = 0;
	for (int i = 1; i <= n; i ++)
		emp += (stat_c[i] == 0);
	if (!emp)
		for (int i = 1; i <= n; i ++)
			if (stat_c[i] != side + 1)
				stat_c[i] = 0;
	msk = encode(stat_c);
}

typedef pair <int, vector <int> > int_v_int;
vector <int> Emp;
int_v_int dfs (int msk, int side) { //side: 0->black, 1->white
	int stat_c[20];
	int_v_int res = MP(-1, Emp); //return : -1: lost, 1: win, 0: draw
	length += (exist[msk] == 0);
	if (exist[msk] == 2) {
		int pans = check_result(msk);
		pans = (side == 0 ? pans : -pans);
		res.F = pans;
		return res;
	}
	exist[msk] ++;
	decode (msk, stat_c);

	//skip
	int_v_int pres = dfs(msk, side ^ 1);
	pres.F *= -1;
	if (pres.F >= res.F)
		res = pres;
	if (res.F == 1 || (res.F == 0 && side)) {
		exist[msk] --;
		length -= (exist[msk] == 0);
		res.S.push_back(msk);
		return res;
	}

	//play
	for (int i = 1; i <= n; i ++)
		if (stat_c[i] == 0) {
			int msk_n = msk + pow3[i - 1] * (side + 1);
			if (msk_n == full_white || msk_n == full_black)
				continue ;
			eliminate (msk_n, side);
			if (exist[msk_n])
				continue ;
			int_v_int pres = dfs (msk_n, side ^ 1);
			pres.F *= -1;
			if (pres.F >= res.F)
				res = pres;
			if (res.F == 1 || (res.F == 0 && side)) {
				exist[msk] --;
				length -= (exist[msk] == 0);
				res.S.push_back(msk);
				return res;
			}
		}
	exist[msk] --;
	length -= (exist[msk] == 0);
	res.S.push_back(msk);
	return res;
}


int main() {
	cin >> n;
	init();
	int_v_int res = dfs (0, 0);
	cerr << "result : " << res.F << endl;
	for (int side = 0, i = res.S.size() - 1; i >= 0; i --, side ^= 1)
		cerr << Print(res.S[i]) << " " << side << endl;
	return 0;
}