蓝桥杯_数学知识_4 (容斥原理 - 博弈论【Nim游戏等】)

890.能被整除的数(容斥原理)

最简单容斥原理:【韦恩图-三个互相相交的圆-分成7个部分】
概率论的加法公式【加上减去重复的】 p(ABC) = p(A) + p(B) + p© - p(AB) - p(BC) - p(AC) + p(ABC)
|S1 + S2 + S3| = |S1| + |S2| + |S3| - |S1^S2| - |S2^S3| - |S1^S3| +|S1S2S3| (||符号表示集合的元素个数)
容斥原理公式: 扩展到n ∑si - ∑|si1^si2|【所有任意两项和】 + ∑|si1si2si3| + … + (-1)m-1 ∑|s1s2s3^… ^sm|
总共的方案数: C(n,1) + C(n,2) + C(n,3) +… C(n,n) = 2n - 1 ,再加C(n,0) = = 2n - - - 时间复杂度
= = 从n个中选出任意多个数的方案数
还可用数学归纳法证明 【学离散数学上下册,等包含全部的编程原理】

给定一个整数n 和m个不同的素数(质数) p 1 ~ p_m ,求1 ~ n 1中能被 p 1 ~ p_m 之一整除的整数有多少个。
输入格式:
第一行包含整数n和m。第二行包含m个质数。
输出格式:
输出一个整数,表示满足条件的整数的个数。
数据范围:
1 <= m <= 16
1 <= n,pi <= 109
输入样例:
10 2
2 3
输出样例:
7

(样例:1-10中能被质数2或3整除的个数有7个)

思路: 利用容斥原理减少时间复杂度 O(2x)
质数的倍数构成的集合
举例:如1~10中能被2,或3整除的个数
S2 = {2,4,6,8,10} , S3 = {3,6,9} , S2^S3 = {6}
|S2+S3| = |S2| + |S3| - |S2^S3| = 5 + 3 - 1 = 7
注意单独求每一项的时间复杂度

容斥原理补充恒等式:
C(k,1) - C(k,2) +… + (-1)k-1C(k,k) = 1 - C(k,0) = 0
1~n中p的倍数的个数 = 向下取整n/p ,能被2,3同时整除的个数 即能被6整除的个数 = 向下取整 n/6
能被p1 … pk个数同时整除的元素的个数 = 向下取整 n/p1p2 … pk
O(m*2m) 【int默认向下取整】

求的是|Sp1+Sp2+Sp3+Sp4+Sp5+Sp6+…+Spk| = 容斥原理 【符号取决于项里有多少个集合,奇数个集合为正 “+”】
简单实现方式: 枚举所有集合情况 - 位运算 1 ~ 2n-1
for(int i =1;i < 2n;i++) 把i看成n位

简记:
	从m个质数中用位运算操作选 ,【1~2^m^- 1  ,每二进制位0/1即: 选/不选】。
	比如m = 2时(质数有2,3),数6 = 11选中了第1,2个质数(2,3)
	【计算过程t = 1 乘质数看能否等于枚举的整数,cnt(选取集合/质数个数)确定符号】



#include <iostream>
using namespace std;
typedef long long LL;
const int N = 20;
int n,m;
int p[N]; 


int main() //所有选法集合个数 , 集合个数奇数符号为负  【 1 ~ 2^n - 1 】(每个选不选)
{
	 //读入
	cin >> n >> m;
	for(int i = 0;i < m;i ++) cin >>p[i];
	
	int res = 0;
	for(int i = 1;i < 1 << m;i++)   // 1 ~ 2^m -1   枚举  
	{
		int t = 1,cnt = 0;
		for(int j = 0;j < m;j++)
			if(i >> j & 1)//判断第j位是不是1   i >> k & 1 
			{
				cnt ++ ; //判断是奇数还是偶数个集合 ,确定符号
				if((LL)t * p[j] > n )  //大于n,不用算了
				{
					t = -1;
					break;	
				} 
				t *= p[j]; //乘积 < n 
			}

		if( t != -1)
		{
			if(cnt % 2) res += n / t; //集合个数为奇数,符号为正 
			else res -= n / t; 
		}
	}
	
	cout << res << endl; 
	
	return 0;
}

【博弈论】 --> Nim游戏【台阶 - 集合 - 拆分】
NIM游戏 【可以转化成有向图游戏】
公平组合游戏ICG
有向图游戏
Mex运算

891 Nim游戏

题目描述:
给定n堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。问如果两人都采用最优策略,先手是否必胜。
输入格式
第一行包含整数n。第二行包含n个数字,其中第 i 个数字表示第 i堆石子的数量。
输出格式
如果先手方必胜,则输出“Yes”。否则,输出“No”。
数据范围
1≤n≤105,1≤每堆石子数 ≤109
输入样例:
2
2 3
输出样例:
Yes

分析:
先给出Nim游戏的一些概念和结论:
给定N堆物品,第i堆物品有Ai个。
两名玩家轮流行动,每次可以任选一堆,取走任意多个物品,
可把一堆取光,但不能不取。取走最后一件物品者获胜。
两人都采取最优策略,问先手是否必胜。

我们把这种游戏称为NIM博弈。
把游戏过程中面临的状态称为局面。
整局游戏第一个行动的称为先手,第二个行动的称为后手。
若在某一局面下无论采取何种行动,都会输掉游戏,则称该局面必败。
所谓采取最优策略是指,若在某一局面下存在某种行动,使得行动后对面面临必败局面,则优先采取该行动。
同时,这样的局面被称为必胜。我们讨论的博弈问题一般都只考虑理想情况,即两人均无失误,都采取最优策略行动时游戏的结果。
NIM博弈不存在平局,只有先手必胜和先手必败两种情况。

定理: NIM博弈先手必胜,当且仅当 A1 ^ A2 ^ … ^ An != 0
也就是说,当各堆石子的个数a1 ^ a2 ^ … ^ an = 0时,先手必败,否则先手必胜。
引用~~
我们先直观的去理解这个定理,对于第i堆石子,其石子个数为x,在x的二进制表示中,至多能为每位贡献一个1.。
比如32位的整数,每一位至多有一个1,这是显然的。a1 ^ a2 ^ … ^ an = 0说明了在二进制的各位,1出现的次数都是偶数的。
先手不论怎么拿,对二进制的各位而言,要么不影响某位1的总数,要么让某位1的总数多1或者少1。
而后手只需要从某堆中拿的石子个数使得石子堆中各位1的总和恢复成偶数即可,
由于各位1的总数开始是偶数,先手不可能将该位的1拿完(除非先手面临的就是必败态:石子已经被拿完了),
所以后手永远有石子可拿,因此先手必败。不妨举个例子,为了表示方便,石子的个数都用二进制表示。
设石子一共有3堆,个数分别为1111,1001,0110,可见初始状态的异或和是0。
假设先手先从第一堆里拿走了1100,第一堆还剩0011,此时二进制中各位1的个数从左往右分别是1,1,2,2.。
为了让前两位的1的个数恢复偶数,后手先想到从某堆里拿走1100,即与先手进行同样的操作,
但是发现此时各堆石子的数目为0011,1001,0110,没有哪个石子堆可以满足后手的需要,于是后手就想将二进制中1的个数变为0,2,2,2。
即从第二堆石子中拿出100个石子,则第二堆还剩0111个石子。
此时二进制各位1的总数又恢复偶数了,重复这类操作,直至先手面临的状态是0,0,0,0,即必败态。
上面不是对这个定理进行严格的证明,只是想从直观的角度去解释这个定理。
下面正式证明该定理:首先必败态,没有石子了,异或和是0。
然后对于任意一种异或和非0的情况,我们都可以用一步操作使之变为异或和为0的情况。
设a1 ^ a2 ^ … ^ an = x != 0,
设x二进制最高位的1是在第k位,则n堆石子中至少有一堆石子的个数该位为1,设第i堆石子个数ai第k位为1,
则ai^x < ai,(将ai的第k为从1变成0了,低位无论怎么变也不会超过原来ai的大小)。
此时我们从第i堆中拿走ai - ai^x个石子,
则第i堆石子个数变为ai - (ai - ai^x)= ai ^ x个,故a1 ^ … ^ ai ^ x ^ … ^an = a1 ^ a2 ^ … ^ an ^ x = x ^ x = 0。
对于任意一种异或和为0的情况,我们拿走任意数量的石子都无法使得石子的异或和还是0.
假设从第i堆拿走一定数量的石子使得ai变为ai’,a1 ^ … ^ ai’ ^ … ^ an = 0,
又a1 ^ … ^ ai ^ … ^ an = 0,将两个式子左右两边分别异或起来得ai’ ^ ai = 0,故ai’ = ai,也就是什么都不拿,这是违背规则的,
故假设不成立。对于异或和为0的状态,我们无法使之一步转化为异或和还是0的状态。
现在我们找到了最终的必败态是石子数量为0,异或和为0.只要先手面对的是异或和非0的状态,
则可以通过一步操作使之转化为异或和为0的状态,后手面对异或和为0的状态无论怎么操作都只能转化为异或和非0的状态,
因此只要先手每次都采用最优策略,面临必败态的一定是后手。

Nim有些完整

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值