博弈,游戏,是否有先手必胜的情况

19 篇文章 0 订阅
9 篇文章 0 订阅

最近做一个题, 说是 判断 某个游戏 是否有先手必胜的情况, 在草稿纸上画了画, 想了一阵,发现 把 所有当前游戏的局面描述为一个状态, 状态之间的变化 实际上构成一颗 多叉树, 如果 有先手必胜的情况, 那么这棵树 就会具有某种特点,即 包含某性质的某颗子树, 然后自己 便陷入 严格定义树的特定,然后 试图编码查找这样的树, 发现 挺恼火。

第二天开会的时候, 发现可以不用管这么多。树中某个节点(也就是局面)对于先手来说是必赢,那么说明这个节点的某个子节点 对于 另一方来说 是必输 局面! 至少有一个这样的子节点就可以了。 而某个节点对某方 是必输局面,说明这个节点的所有子节点对另一方而言 都是 必赢局面。

分析到这里, 就是 两个函数而已, 需要互相调用对方, 形成一种间接的递归。这里具体是什么游戏其实是无关紧要的。很多类似的题目都能用这样的方法解决。

你和朋友玩一个叫做「翻转游戏」的游戏,游戏规则:给定一个只有 + 和 - 的字符串。你和朋友轮流将 连续 的两个 “++” 反转成 “–”。 当一方无法进行有效的翻转时便意味着游戏结束,则另一方获胜。
输入: s = “++++”
输出: true
解析: 起始玩家可将中间的 “++” 翻转变为 “±-+” 从而得胜。
https://leetcode-cn.com/problems/flip-game-ii

代码里面看不到 任何 树的痕迹, 但实际变化在逻辑上是一棵树, 如果要分析时间复杂度,那么必须借助树的结构来分析。

全部代码如下:


#include "haithink_common.h"

// 返回true, 则必赢
// 返回false, 不能必赢
bool _canWin(string & s) {

	// 构造可能的变换, 如果有一个 必输的叶子节点,那么 返回true, 否则返回false
	for (int i = 0; i < s.length() - 1; i++) {
		if (s[i] == '+' && s[i + 1] == '+') {
			string ss = s;
			ss[i] = '-';
			ss[i + 1] = '-';

			if (canLose(ss)) {
				return true;
			}
		}
	}

	return false;

}

// 返回true, 则必输
// 返回 false,不是必输
bool canLose(string & s) {

	// 没有子节点, 那么必输
	// 如果有子节点,那么每个子节点都必赢,
	// 如果某个子节点不是必赢, 那么 不是必输
	for (int i = 0; i < s.length() - 1; i++) {
		if (s[i] == '+' && s[i + 1] == '+') {
			string ss = s;
			ss[i] = '-';
			ss[i + 1] = '-';

			if (_canWin(ss) == false) {
				return false;
			}
		}
	}

	return true;
}


bool canWin(string s) {

	if (s.length() < 2) {
		return false;
	}

	return _canWin(s);
}


int main() {

	string s = "-+++--+++";

	output(canWin(s));

	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值