博弈论学习笔记【施工中】

news2024/11/25 23:37:33

SG函数

首先定义就不用我讲了吧,还不会的自己看看 传送门

再进一步理解一下吧:
在这里插入图片描述
黑色数字是节点编号,红色是 S G SG SG 函数值

看下它的过程:

  1. 首先 5 5 5 6 6 6 没有后继节点,为必败态,先赋值为 0 0 0
  2. 3 3 3 只有 6 6 6 一个后继节点,计算得 1 1 1
  3. 现在我们已经得出了 2 2 2 4 4 4 的所有后继节点的值,进而给它赋值
  4. 最后统计 1 1 1 的子节点中含有的值有 0 , 1 , 2 0,1, 2 012 ,取值 3 3 3

最关键是定义知道了这个东西怎么用呢?首先我们需要明白两点:

  1. S G SG SG 函数是 x o r xor xor 起来用的,故而可能存在两种状态 x o r xor xor 起来并不能达到必胜/必败态,故而不能仅仅只赋值为 0 0 0 1 1 1
  2. S G SG SG 函数的值是可以由我们任意决定的,我们主要通过搜索的方式进行赋值。

那么我们来抓个典型

【SSLOJ 1633】B君的游戏

B君和L君要玩一个游戏。刚开始有 n n n 个正整数 a i a_i ai

双方轮流操作。每次操作,选一个正整数 x x x,将其移除,再添加 7 7 7 个数字 x 1 , x 2 . . . x 7 x_1,x_2...x_7 x1,x2...x7 。要求对于 x i x_i xi满足 0 < = x i < x 0<=x_i<x 0<=xi<x x & x i = x i x \& x_i = x_i x&xi=xi

注意 0 0 0不能被选取,所以这个游戏一定会结束,而谁无法操作谁就失败。

B君根据自己的经验,认为先手胜率高一点,所以B君是先手。

B君想知道自己是否必胜。

第一行一个整数 n ( 1 < = n < = 100000 ) n (1 <= n <= 100000) n(1<=n<=100000)

以下 n n n n n n 个数 a i ( 0 < = a i < 2 64 ) a_i (0 <= a_i < 2^{64}) ai(0<=ai<264)

输入样例

4
1
2
3
4

输出样例

B

基本思路

很显然,后继状态及结果至于 x x x 1 1 1 的个数有关,因为本质就是从 x x x 中选几个 1 1 1 出来嘛。这题我们暴力打出每种 1 1 1 的个数下的 S G SG SG 函数就可以了。

上代码,我们也借此了解一下 S G SG SG 函数是如何被计算的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
const int M=7e7+10;
int sg[70];
bool vis[M]; 
inline void dfs(int num,int p,int ans,int cur){
//	搜索求解sg函数值 
//	num限制最大值,cur限制最小值,p控制递归层数
	if(p==0){
		vis[ans]=true;
		return;
	}
	for(int i=cur;i<num;i++){
		dfs(num,p-1,ans^sg[i],i);
	}
}
inline void init(){
	printf("{");
	sg[0]=0;
	for(int i=1;i<=64;i++){
		memset(vis,0,sizeof(vis));
		dfs(i,7,0,0);
		for(int j=0;j<M;j++){
			if(vis[j]==false){
				sg[i]=j;
				break;
			}
		}
		cout<<i<<":";
		printf("%d",(sg[i]));
		if(i<64) printf(",");
	}
	cout<<"}";
}
int main(){
//	ios::sync_with_stdio(false);
	init();
	return 0;
}

我们先仔细看看搜索部分

inline void dfs(int num,int p,int ans,int cur){
//	搜索求解sg函数值 
//	num限制最大值,cur限制最小值,p控制递归层数
	if(p==0){
		vis[ans]=true;
		return;
	}
	for(int i=cur;i<num;i++){
		dfs(num,p-1,ans^sg[i],i);
	}
}

Q1:为什么要用 p p p 来限制递归层数,而且可以在 i n i t init init 中看到限制了 7 7 7 层?

这个 7 7 7 就是题目中说的选出 7 7 7 个数,因为选出来的数 1 1 1 的个数都比 x x x 要少,故而他们的 S G SG SG 值都算好了,将他们 x o r xor xor 起来就可以得到当前答案了。

Q2:为什么要将他们的后继节点的 S G SG SG 值都异或起来呢?

因为我们可以发现实际上 S G SG SG 函数的用法就是异或起来,据此判断先后手必胜,详细证明会很麻烦。

O3为什么还要限制最小值呢?

因为仔细观察你就会发现这实际上是将全排列转换成了组合计数,减少了时间复杂度,删去了重复的递归,因为后面选择的数是严格大于等于前面选择的数的。

Q4 v i s vis vis 数组是干什么用的?

这个显然就是进行 m e x mex mex 运算的,记录后继状态中有那些值,然后向 i n i t init init 中求解。

最后输出就像这样:

{1:1,2:2,3:4,4:8,5:16,6:32,7:64,8:128,9:255,10:256,11:512,12:1024,13:2048,14:3855,15:4096,16:8192,17:13107,18:16384,19:21845,20:27306,21:32768,22:38506,23:65536,24:71576,25:92115,26:101470,27:131072,28:138406,29:172589,30:240014,31:262144,32:272069,33:380556,34:524288,35:536169,36:679601,37:847140,38:1048576,39:1072054,40:1258879,41:1397519,42:2005450,43:2097152,44:2121415,45:2496892,46:2738813,47:3993667,48:4194304,49:4241896,50:4617503,51:5821704,52:7559873,53:8388608,54:8439273,55:8861366,56:11119275,57:11973252,58:13280789,59:16777216,60:16844349,61:17102035,62:19984054,63:21979742,64:23734709}
--------------------------------
Process exited after 104 seconds with return value 0
请按任意键继续. . .

我这里还加了编号,用的时候自然得去掉。

核心代码

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
ull sg[70]={0,1,2,4,8,16,32,64,128,255,256,512,
			1024,2048,3855,4096,8192,13107,16384,
			21845,27306,32768,38506,65536,71576,
			92115,101470,131072,138406,172589,240014,
			262144,272069,380556,524288,536169,679601,
			847140,1048576,1072054,1258879,1397519,
			2005450,2097152,2121415,2496892,2738813,
			3993667,4194304,4241896,4617503,5821704,
			7559873,8388608,8439273,8861366,11119275,
			11973252,13280789,16777216,16844349,
			17102035,19984054,21979742,23734709};
ull n,a,ans,sum;
int main() {
	ios::sync_with_stdio(false);
	cin>>n;
	while(n--){
		cin>>a;
		sum=0;
		while(a){
			a^=(a&(-a));
			sum++;
		}
		ans^=sg[sum];
	}
	if(ans) cout<<"B";
	else cout<<"L";
	return 0;
}

再抓个典型巩固一下

【luogu P10501】 Cutting Game

P.S.进一步可点击链接前往往洛谷参考题解。

题面翻译

【题目描述】

Urej 喜欢玩各种类型的沉闷游戏。他通常会邀请其他人和他一起玩。他说,玩这些游戏可以展现他的非凡智慧。最近,Urej 对一个新游戏产生了极大兴趣,而 Erif Nezorf 成为了牺牲品。为了摆脱玩这样一个沉闷游戏的痛苦,Erif Nezorf 请求你的帮助。这个游戏使用一个由 W × H W \times H W×H 格网组成的矩形纸张。两名玩家轮流将纸张切割成两个矩形部分。在每个回合中,玩家可以横向或纵向切割,保持每个格网完整。经过 N N N 轮后,纸张将被切割成 N + 1 N+1 N+1 片,然后在后续的回合中,玩家可以选择任意一片进行切割。如果一名玩家切割出一个只有一个格网的纸片,他就赢得了游戏。如果这两个人都非常清楚,你应该写一个问题来告诉是否先手的玩家能赢得游戏。

【输入格式】

输入包含多个测试用例。每个测试用例在一行中只包含两个整数 W W W H ( 2 ≤ W , H ≤ 200 ) H (2 \le W , H \le 200) H(2W,H200),分别表示原始纸张的宽度和高度。

【输出格式】

对于每个测试用例,应该只输出一行。如果先手的玩家能赢得游戏,则输出 “WIN”,否则输出 “LOSE”。

样例 #1

样例输入 #1

2 2
3 2
4 2

样例输出 #1

LOSE
LOSE
WIN

基本思路

这个也很显然,状态是什么?就是一张纸的长和宽嘛,长和宽就对应了它的 S G SG SG 值。可能有同学会疑问,可是我们裁出的是两张纸啊,不一定非要再这张上进行操作,在另一张纸上裁一刀周旋一下嘛。问题是我们最后要把各个状态的 S G SG SG 值异或起来啊,一定是会把两个状态(两张纸)异或起来才是答案嘛

好了,就像上面说的,这次还是搜索求 S G SG SG 值吗?显然不会是,我们会用递归(这两个思想还是有点区别的)。上一道题的搜索是在搜什么?就是排列组合它的状态啊,我们这道题还要用上面这种方式找出它的所有后继状态吗?我们裁出的是两张,那直接 f o r for for 循环枚举裁出的长度就可以了。但这道题一定要注意,不能裁出含有一边长度为 1 1 1 的,那就是必败了(显然不符合博弈论双方都采用最优措施的设定了)

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=210;
const int S=7e4+10;
int sg[N][N],w,h;
inline int SG(int x,int y){
	if(sg[x][y]!=-1) return sg[x][y];
	bool vis[S];
	memset(vis,0,sizeof(vis));
	for(int i=2;i<x-1;i++) vis[SG(i,y)^SG(x-i,y)]=true;
	for(int i=2;i<y-1;i++) vis[SG(x,i)^SG(x,y-i)]=true;
	//计算
	for(int i=0;i<S;i++)if(vis[i]==false)
		return sg[x][y]=i;
}
int main(){
	ios::sync_with_stdio(false);
	memset(sg,-1,sizeof(sg));
	while(cin>>w>>h){
		if(SG(w,h)==0) cout<<"LOSE\n";
		if(SG(w,h)) cout<<"WIN\n";
	}
    return 0;
}

大家也可以看到这次不是预处理 S G SG SG 值了,而是在线算法求解,如果碰到哪个 S G SG SG 值还不知道的就直接递归求解就可以啦。

也可能有同学会疑惑对于必败点的问题, 1 ∗ 1 1*1 11 是必胜点,那么我们可以发现 2 ∗ 2 , 2 ∗ 3 , 3 ∗ 2 2*2 , 2*3 ,3*2 22,23,32 就是必败点了,因为怎么裁都会裁出 1 ∗ X 1*X 1X 的纸。这里就不用考虑这个问题,因为到上述状态是因为会裁处一边为 1 1 1 ,就无法计算,会自动赋值为 0 0 0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2219945.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV和HALCON

OpenCV和HALCON是两种广泛用于图像处理和计算机视觉的开发库&#xff0c;它们各有优缺点&#xff0c;适合不同的应用场景。以下是两者的比较&#xff1a; 1. 开发背景与定位 OpenCV (Open Source Computer Vision Library)&#xff1a; 开源库&#xff0c;最初由Intel开发&…

【图解版】力扣第146题:LRU缓存

力扣第146题&#xff1a;LRU缓存 一、LRU算法1. 基本概念2. LRU 和 LFU 的区别&#xff1a;3. 为什么 LRU 不需要记录使用频率&#xff1f; 二、Golang代码实现三、代码图解1. LRUCache、DLinkedNode两个结构体2. 初始化结构体对象3. addToHead函数4. removeNode函数5. moveToH…

基于单片机的多功能鱼缸控制系统设计

本设计以STC12C5A60S2单片机为核心的多功能鱼缸控制系统&#xff0c;该系统可分别利用温度传感器、水位传感器和浑浊度传感器来检测鱼缸内部的水温、液体高度和浑浊程度&#xff0c;并在显示屏上进行显示。若检测结果超出阈值范围&#xff0c;则继电器工作从而控制内部环境。通…

LeetCode102. 二叉树的层序遍历(2024秋季每日一题 43)

给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]] 示例 2&#xff1a; 输入…

白嫖正版xshell和XFTP

在哪里可以下载正版免费的xshell和XFTP&#xff0c;并且还能够获得官网免费持久更新 白嫖步骤 首先直接在浏览器搜索xshell官网 点进官网之后直接点击下载 接着点击免费授权页面 进入之后就可以免费下载了 下载安装完成后填写用户名和邮箱并提交&#xff0c;这里就以xshell为…

Veritas NetBackup 10.5 发布,新增功能概览

Veritas NetBackup 10.5 发布&#xff0c;新增功能概览 Veritas NetBackup 10.5 (Unix, Linux, Windows) - 领先的企业备份解决方案 The #1 enterprise backup and recovery solution. 请访问原文链接&#xff1a;https://sysin.org/blog/veritas-netbackup-10/ 查看最新版。…

EditPlus的安装软件包

解压并粘贴到C:\Program Files (x86)中 点击激活密匙,并一直同意 确认并选择默认的位置: 关闭并重新激活密匙 就好了 无需添加快捷方式: 只需要选择任意文件 并选择该应用打开一次即可 通过百度网盘分享的文件&#xff1a;EditPlus_5.0.611.zip 链接&#xff1a;https://pa…

在Debian 11/Debian 10上安装MySQL 5.7

本文借鉴 如何在 Debian 11/Debian 10 上安装 MySQL 5.7 |https://cn.linux-console.net/?p20728 下载安装存储库 安装 根据提示选择mysql5.7即可(会车键选择) wget https://dev.mysql.com/get/mysql-apt-config_0.8.16-1_all.debsudo dpkg -i mysql-apt-config_0.8.16-1_a…

MFC工控项目实例二十四模拟量校正值输入

承接专栏《MFC工控项目实例二十三模拟量输入设置界面》 对模拟量输入的零点校正值及满量程对应的电压值进行输入。 1、在SenSet.h文件中添加代码 #include "BtnST.h" #include "ShadeButtonST.h"/ // SenSet dialogclass SenSet : public CDialog { // Co…

AWD初步学习

一般的AWD不提供外网环境&#xff0c; AWD比赛中一般准备语言环境&#xff0c;工具、exploit及相关脚本框架。 1.脚本环境 一般在/var/www/html目录的下面&#xff0c;需要提前PHP和常用的Web开发语言环境在本地进行配置&#xff0c;且统一语言尽量多配置环境&#xff0c;比如P…

基于stm32的4G模块点灯实验

led模块功能封装 #include "led.h" #include "sys.h"//初始化GPIO函数 void led_init(void) {GPIO_InitTypeDef gpio_initstruct;//打开时钟__HAL_RCC_GPIOB_CLK_ENABLE();//调用GPIO初始化函数gpio_initstruct.Pin GPIO_PIN_8 | GPIO_PIN_9;gpio_inits…

如何将LiDAR坐标系下的3D点投影到相机2D图像上

将激光雷达点云投影到相机图像上做数据层的前融合&#xff0c;或者把激光雷达坐标系下标注的物体点云的3d bbox投影到相机图像上画出来&#xff0c;都需要做点云3D点坐标到图像像素坐标的转换计算&#xff0c;也就是LiDAR 3D坐标转像素坐标。 看了网上一些文章都存在有错误或者…

Android 第5种启动模式:singleInstancePerTask

Android 第5种启动模式&#xff1a;singleInstancePerTask 随着 Android 版本的更新&#xff0c;应用启动模式逐渐丰富。在 Android 12 中&#xff0c;新增了一种启动模式——singleInstancePerTask。它是继 standard、singleTop、singleTask 和 singleInstance 之后的第五种启…

一起搭WPF架构之LiveCharts.Wpf的简单了解与安装

一起搭WPF架构之LiveCharts.Wpf的简单了解与安装 前言LiveCharts.Wpf介绍LiveCharts.Wpf的安装总结 前言 根据项目需求&#xff0c;我单独留了一个界面用于进行数据分析。数据分析的内容考虑是采用图表的形式将SQLite数据库中存储的数据进行绘制成图&#xff0c;以便数据分析。…

HTB:Broker[WriteUP]

目录 连接至HTB服务器并启动靶机 1.Which open TCP port is running the ActiveMQ service? 使用fscan对靶机开放端口进行扫描 使用nmap对靶机开放端口进行脚本、服务信息扫描 2.What is the version of the ActiveMQ service running on the box? 3.What is the 2023 …

windows下安装VirtualBox7.1.4

记录详细的安装过程与遇到的问题&#xff1b; 下载地址 virtualbox官网 清华镜像源下载 下载完成后文件&#xff1a; 双击打开&#xff1b; 报错了 意思是需要pc上先安装Microsoft Visual C 2019 https://learn.microsoft.com/zh-cn/cpp/windows/latest-supported-vc-redi…

C++ 中的线程、锁、条件变量。

线程 0.Linux中有POSIX标准的线程&#xff0c;Boost库中也支持线程&#xff08;比如thread_group 和 upgrade_lock &#xff09;&#xff0c;C11<thread>头文件中也提供了相应的API支持我们使用线程。它们三个&#xff0c;你学会一个&#xff0c;自然触类旁通。 1.创建…

Java-类与对象-下篇

关于类与对象&#xff0c;内容较多&#xff0c;我们分为两篇进行讲解&#xff1a; &#x1f4da; Java-类与对象-上篇&#xff1a;————<传送门:Java-类与对象-上篇-CSDN博客> &#x1f4d5; 面向对象的概念 &#x1f4d5; 类的定义格式 &#x1f4d5; 类的使用 …

特斯拉Optimus:展望智能生活新篇章

近日&#xff0c;特斯拉举办了 "WE ROBOT" 发布会&#xff0c;发布会上描绘的未来社会愿景&#xff0c;让无数人为之向往。在这场吸引全球无数媒体的直播中&#xff0c;特斯拉 Optimus 人形机器人一出场就吸引了所有观众的关注。从多家媒体现场拍摄的视频可以看出来&…

【C++】C++11新特性——右值引用,来看看怎么个事儿

&#x1f680;个人主页&#xff1a;小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言一、左值引用和右值引用二、右值引用和移动语义2.1 移动构造2.2 移动赋值2.3 STL容器插入接口2.4 左值右值相互…