算法很美之数学问题

第六篇 算法很美之数学问题

题目1 : 博弈游戏·Nim游戏

描述

今天我们要认识一对新朋友,Alice与Bob。
Alice与Bob总是在进行各种各样的比试,今天他们在玩一个取石子的游戏。
在这个游戏中,Alice和Bob放置了N堆不同的石子,编号1…N,第i堆中有A[i]个石子。
每一次行动,Alice和Bob可以选择从一堆石子中取出任意数量的石子。至少取1颗,至多取出这一堆剩下的所有石子。
Alice和Bob轮流行动,取走最后一个石子的人获得胜利。
假设每一轮游戏都是Alice先行动,请你判断在给定的情况下,如果双方都足够聪明,谁会获得胜利?

提示:Nim?!

对于这个游戏有一个非常神奇的结论:

对于一个局面,当且仅当A[1] xor A[2] xor ... xor A[N] = 0时,该局面为P局面。

输入

第1行:1个整数N。表示石子堆数。1≤N≤100
第2行:N个整数,第i个整数表示第i堆石子的个数A[i],1≤A[i]≤10000
输出

第1行:1个字符串,若Alice能够获胜输出"Alice",否则输出"Bob"
样例输入

3
3 2 1

样例输出

Bob
#include<stdio.h>
main()
{
	int n,a[10005],fl=0,i;
	scanf("%d",&n);
	for(i=0;i<n;i++)
	{
		scanf("%d",&a[i]);
		fl^=a[i];
	}
	if(fl==0)printf("Bob");
	else printf("Alice");
	
	return 0;
} 

题目2 : 数论二·Eular质数筛法

描述

小Ho:小Hi,上次我学会了如何检测一个数是否是质数。于是我又有了一个新的问题,我如何去快速得求解[1,N]这个区间内素数的个数呢?

小Hi:你自己有什么想法么?

小Ho:有!我一开始的想法是,自然我们已经知道了如何快速判定一个数是否是质数,那么我就直接将[1,N]之间每一个数判定一次,就可以得到结果。但我发现这个方法太笨了。

扫描二维码关注公众号,回复: 5692029 查看本文章

小Hi:确实呢,虽然我们已经通过快速素数检测将每一次判定的时间复杂度降低,但是N个数字的话,总的时间复杂度依旧很高。

小Ho:是的,所以后来我改变了我的算法。我发现如果一个数p是质数的话,那么它的倍数一定都是质数。所以我建立了一个布尔类型的数组isPrime,初始化都为true。我从2开始枚举,当我找到一个isPrime[p]仍然为true时,可以确定p一定是一个质数。接着我再将N以内所有p的倍数全部设定为isPrime[p*i]=false。

写成伪代码为:

isPrime[] = true
primeCount = 0
For i = 2 … N
If isPrime[i] Then
primeCount = primeCount + 1
multiple = 2
While (i * multiple ≤ N)
isPrime[i * multiple] = false
multiple = multiple + 1
End While
End If
End For

小Hi:小Ho你用的这个算法叫做Eratosthenes筛法,是一种非常古老的质数筛选算法。其时间复杂度为O(n log log n)。但是这个算法有一个冗余的地方:比如合数10,在枚举2的时候我们判定了一次,在枚举5的时候我们又判定了一次。因此使得其时间复杂度比O(n)要高。

小Ho:那有没有什么办法可以避免啊?

小Hi:当然有了,一个改进的方法叫做Eular筛法,其时间复杂度是O(n)的。

提示:Eular质数筛法
输入

第1行:1个正整数n,表示数字的个数,2≤n≤1,000,000。
输出

第1行:1个整数,表示从1到n中质数的个数
样例输入

9

样例输出

4

Eular质数筛法的伪代码:

isPrime[] = true
primeList = []
primeCount = 0
For i = 2 .. N
	If isPrime[i] Then
		primeCount = primeCount + 1
		primeList[ primeCount ] = i
	End If 
	For j = 1 .. primeCount
		If (i * primeList[j] > N) Then
			Break
		End If
		isPrime[ i * primeList[j] ] = false
		If (i % primeList[j] == 0) Then
			Break
		End If
	End If
End For

附本人AC代码

#include<stdio.h>
#include<string.h>
char isPrime[1000001];
int primeList[78498];
int main(){
	int N, i, j, primeCount;
	scanf("%d", &N);
	memset(isPrime, 1, sizeof(isPrime));
	primeCount = 0;
	for(i = 2; i <= N; i++){
		if(isPrime[i]){
		primeCount++;
		primeList[primeCount] = i;
		}
		for(j = 1; j <= primeCount && i*primeList[j] <= N; j++){
			isPrime[i*primeList[j]] = 0;
			if(i % primeList[j] == 0){
			break;
			}
		}
	}
			printf("%d\n", primeCount);
	       return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_44391957/article/details/87903008
今日推荐