洛谷 P1290 欧几里德的游戏 SG 函数的另类求解

传送门
我们用 n, m (n>m) 来表示当前状态
显然如果 m = 0 的话是输了,上次对手已经喝完了一个
所以这个状态你输了
那么考虑 n, m的后继状态有哪些?
(n-m,m) (n-2*m,m) (n-3*m,m) .....(n-k*m,m) (m, n%m)
所以 SG[n][m] = mex{SG(n-m,m) SG(n-2*m,m) SG(n-3*m,m) .....SG(n-k*m,m) SG(m, n%m)}
那么 SG(n-m,m) 怎么求呢
SG[n-m][m] = mex{SG(n-2*m,m) SG(n-3*m,m) .....SG(n-k*m,m) SG(m, n%m)}
是的可以递归下去发现,全部和 SG(m, n%m) 有关
如果 SG(m, n%m) == 0, 那么 SG(n-km,m) 是 1, 其余分别是2,3,4…
所以肯定非 0 ,简单用 true 表示
如果 SG(m, n%m) != 0 ,那么 SG(n-k
m,m) 是 0, 其余也就肯定不为 0 了
所以只有 n/m 为 1(即 n 小于 m 的两倍)时,当前 (n,m) 结果与 SG(m, n%m) 相反,其余都是 true
那么辗转递归的求一下就可以了

#include 
using namespace std;
bool getsg(int n, int m) {
	if (m == 0)
		return false;
	if (n/m == 1)
		return !getsg(m, n%m);
	else
		return true;
}
int main() {
	int T;
	cin >> T;
	int n, m;
	while (T--) {
		cin >> n >> m;
		if (getsg(max(n, m), min(n, m)))
			cout << "Stan wins" << endl;
		else
			cout << "Ollie wins" << endl;
	}
	return 0;
}

这里提一下,如果用普通的方法求 SG 似乎不是很方便
因为中止状态你不太好确定
比如当有倍数关系存在的时候,你再把它分成各个子游戏再求 mex 是不合适的,因为你肯定已经赢了,是不能分解成独立子游戏的!子游戏不再是独立的了!!!
这个和 cutting game 有点类似,我的 cutting game 的题解里也有提到这个问题。

你可能感兴趣的:(杂谈)