假设围棋棋盘是完全图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;
}



posted @ 2020-12-30 15:55  敲键盘贼快  阅读(149)  评论(0)    收藏  举报