hiho一下 第九十四周 数论三·约瑟夫问题
数论三·约瑟夫问题
描述
小Hi和小Ho的班级正在进行班长的选举,他们决定通过一种特殊的方式来选择班长。
首先N个候选人围成一个圈,依次编号为0..N-1。然后随机抽选一个数K,并0号候选人开始按从1到K的顺序依次报数,N-1号候选人报数之后,又再次从0开始。当有人报到K时,这个人被淘汰,从圈里出去。下一个人从1开始重新报数。
也就是说每报K个数字,都会淘汰一人。这样经过N-1轮报数之后,圈内就只剩下1个人了,这个人就作为新的班长。
举个例子,假如有5个候选人,K=3:
初始
0: 0 1 2 3 4
从0号开始报数,第1次是2号报到3
1: 0 1 - 3 4 // 0 1 2, 2号候选人淘汰
从3号开始报数,第2次是0号报到3
2: - 1 3 4 // 3 4 0, 0号候选人淘汰
从1号开始报数,第3次是4号报到3
3: 1 3 - // 1 3 4, 4号候选人淘汰
从1号开始报数,第4次是1号报到3
4: - 3 // 1 3 1, 1号候选人淘汰
对于N=5,K=3的情况,最后当选班长的人是编号为3的候选人。
小Ho:小Hi,我觉得当人数和K都确定的时候已经可以确定结果了。
小Hi:嗯,没错。
小Ho:我也想当班长,小Hi你能提前告诉我应该站在哪个位置么?
小Hi:我可以告诉你怎么去求最后一个被淘汰的位置,不过具体的值你得自己去求解。
小Ho:嗯,没问题,那么你快告诉我方法吧!
输入
第1行:1个正整数t,表示多组输入数据,1≤t≤100
第2..t+1行:每行2个正整数n,k,第i+1行表示第i组测试数据,2≤n≤1,000,000,000。2≤k≤1,000
输出
第1..t行:每行1个整数,第i行表示第i组数据的解
- 样例输入
-
2
5 3
8 3 - 样例输出
-
3
6
解答:
提示:约瑟夫问题
小Hi:这个问题其实还蛮有名的,它被称为约瑟夫的问题。
最直观的解法是用循环链表模拟报数、淘汰的过程,复杂度是O(NM)。
今天我们来学习两种更高效的算法,一种是递推,另一种也是递推。第一种递推的公式为:
令f[n]表示当有n个候选人时,最后当选者的编号。
f[1] = 0
f[n] = (f[n - 1] + K) mod n
接下来我们用数学归纳法来证明这个递推公式的正确性:
(1) f[1] = 0
显然当只有1个候选人时,该候选人就是当选者,并且他的编号为0。
(2) f[n] = (f[n - 1] + K) mod n
假设我们已经求解出了f[n - 1],并且保证f[n - 1]的值是正确的。
现在先将n个人按照编号进行排序:
0 1 2 3 ... n-1
那么第一次被淘汰的人编号一定是K-1(假设K < n,若K > n则为(K-1) mod n)。将被选中的人标记为"#":
0 1 2 3 ... K-2 # K K+1 K+2 ... n-1
第二轮报数时,起点为K这个候选人。并且只剩下n-1个选手。假如此时把k+1看作0',k+2看作1'...
则对应有:
0 1 2 3 ... K-2 # K K+1 K+2 ... n-1
n-K' n-2' 0' 1' 2' ... n-K-1'
此时在0',1',...,n-2'上再进行一次K报数的选择。而f[n-1]的值已经求得,因此我们可以直接求得当选者的编号s'。
但是,该编号s'是在n-1个候选人报数时的编号,并不等于n个人时的编号,所以我们还需要将s'转换为对应的s。
通过观察,s和s'编号相对偏移了K,又因为是在环中,因此得到s = (s'+K) mod n。
即f[n] = (f[n-1] + k) mod n。
至此递推公式的两个式子我们均证明了其正确性,则对于任意给定的n,我们可以使用该递推式求得f[n],写成伪代码为:
Josephus(N, K):
f[1] = 0
For i = 2 .. N
f[i] = (f[i - 1] + K) mod i
End For
Return f[N]
同时由于计算f[i]时,只会用到f[i-1],因此我们还可以将f[]的空间节约,改进后的代码为:
Josephus(N, K):
ret = 0
For i = 2 .. N
ret = (ret + K) mod i
End For
Return ret
该算法的时间复杂度为O(N),空间复杂度为O(1)。对于N不是很大的数据来说,可以解决。
小Ho:要是N特别大呢?
小Hi:那么我们就可以用第二种递推,解决的思路仍然和上面相同,而区别在于我们每次减少的N的规模不再是1。
同样用一个例子来说明,初始N=10,K=4:
初始序列:
0 1 2 3 4 5 6 7 8 9
当7号进行过报数之后:
0 1 2 - 4 5 6 - 8 9
在这里一轮报数当中,有两名候选人退出了。而对于任意一个N,K来说,退出的候选人数量为N/K("/"运算表示整除,即带余除法取商)
由于此时起点为8,则等价于:
2 3 4 - 5 6 7 - 0 1
因此我们仍然可以从f[8]的结果来推导出f[10]的结果。
但需要注意的是,此时f[10]的结果并不一定直接等于(f[8] + 8) mod 10。
若f[8]=2,对于原来的序列来说对应了0,(2+8) mod 10 = 0,是对应的;若f[8]=6,则有(6+8) mod 10 = 4,然而实际上应该对应的编号为5。
这是因为在序列(2 3 4 - 5 6 7 - 0 1)中,数字并不是连续的。
因此我们需要根据f[8]的值进行分类讨论。假设f[8]=s,则根据s和N mod K的大小关系有两种情况:
1) s < N mod K : s' = s - N mod K + N
2) s ≥ N mod K : s' = s - N mod K + (s - N mod K) / (K - 1)
此外还有一个问题,由于我们不断的在减小N的规模,最后一定会将N减少到小于K,此时N/K=0。
因此当N小于K时,就只能采用第一种递推的算法来计算了。
最后优化方法的伪代码为:
Josephus(N, K):
If (N == 1) Then
Return 0
End If
If (N < K) Then
ret = 0
For i = 2 .. N
ret = (ret + K) mod i
End For
Return ret
End If
ret = Josephus(N - N / K, K);
If (ret < N mod K) Then
ret = ret - N mod K + N
Else
ret = ret - N mod K + (ret - N mod K) / (K - 1)
End If
Return ret
改进后的算法可以很快将N的规模减小到K,对于K不是很大的问题能够快速求解。
#include<iostream>
#include<list>
#include<cstdlib>
using namespace std; int solve(int total,int key){ list<int>* table = new list<int>(); for (int i = ; i < total; i++)
{
table->push_back(i);
} int shout = ;
for (list<int>::iterator it = table->begin(); table->size() != ;)
{
if (shout++ == key)
{
it = table->erase(it);
shout = ;
}
else
{
++it;
} if (it == table->end())
{
it = table->begin();
}
}
return *table->begin(); }
int main(int argc, char* argv[])
{
int n, total, key;
cin>>n; while (n--){
cin >> total >> key; cout << solve(total,key) << endl;
}
return ;
}
后来改的递推,效率提高了许多,AC了,这也体现了算法设计中,好的算法,不单单要解决实际问题,还要考虑效率问题,高效合理。
#include<iostream>
#include<list>
#include<cstdlib>
using namespace std; int solve(int total, int key)
{
if (total == )
return ;
if (total < key){
int anw = ;
for (int i = ; i <= total; i++)
anw = (anw + key) % i;
return anw;
} int anw = solve(total - total / key,key);
int temp = total % key;
if (anw < temp)
anw = anw - temp + total;
else
anw = anw - temp + (anw - temp) / (key - );
return anw;
} int main(int argc, char* argv[])
{
int n, total, key;
cin >> n; while (n--){
cin >> total >> key;
cout << solve(total, key) << endl;
}
return ;
}
#include "iostream" using namespace std; int n, l; int getlive(int i, int m)//i长 m数
{
if (i == )
return ;
return (getlive(i - , m) + m - ) % i + ;
} int main()
{
while (true)
{
cin >> n >> l;
cout<<getlive(n, l);
}
}
hiho一下 第九十四周 数论三·约瑟夫问题的更多相关文章
- hiho一下 第九十五周 数论四·扩展欧几里德
题目 : 数论四·扩展欧几里德 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho周末在公园溜达.公园有一堆围成环形的石板,小Hi和小Ho分别站在不同的石板上 ...
- hiho一下 第九十六周 数论五·欧拉函数
题目1 : 数论五·欧拉函数 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho有时候会用密码写信来互相联系,他们用了一个很大的数当做密钥.小Hi和小Ho约定 ...
- hiho一下 第九十七周 数论六·模线性方程组
题目1 : 数论六·模线性方程组 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho:今天我听到一个挺有意思的故事! 小Hi:什么故事啊? 小Ho:说秦末,刘邦的将军 ...
- hiho一下 第九十八周 搜索一·24点
题目1 : 搜索一·24点 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 周末,小Hi和小Ho都在家待着. 在收拾完房间时,小Ho偶然发现了一副扑克,于是两人考虑用这副 ...
- 【hihocoder 1296】数论三·约瑟夫问题
[题目链接]:http://hihocoder.com/problemset/problem/1296 [题意] [题解] [Number Of WA] 0 [完整代码] #include <b ...
- 第十四周实验报告:实验四 Android程序设计
20162317袁逸灏 第十四周实验报告:实验四 Android程序设计 实验内容 Android Studio 实验要求 学会使用Android Studio 学习 活动 以及相关知识内容 学习 U ...
- 杨其菊201771010134《面向对象程序设计(java)》第十四周学习总结
第十四周学习总结 第一部分:理论知识 理论知识:本周学习Swing用户界面 内容:Swing与模型-视图-控制器设计模式:布局管理概述:文本输入 :选择组件:菜单:复杂的布局管理:对话框: 第二部分: ...
- 「kuangbin带你飞」专题十四 数论基础
layout: post title: 「kuangbin带你飞」专题十四 数论基础 author: "luowentaoaa" catalog: true tags: mathj ...
- 201871010111-刘佳华《面向对象程序设计(java)》第十四周学习总结
201871010111-刘佳华<面向对象程序设计(java)>第十四周学习总结 实验十二 Swing图形界面组件(一) 实验时间 2019-11-29 第一部分:基础知识总结 1.设计 ...
随机推荐
- EntityFramework_MVC4中EF5 新手入门教程之六 ---6.通过 Entity Framework 更新关联数据
在前面的教程中,您将显示相关的数据 :在本教程中,您会更新相关的数据.对于大多数的关系,这个目标是可以通过更新相应的外键字段来达到的.对于多对多关系,实体框架并不直接,暴露联接表,因此您必须显式添加和 ...
- javascript去掉字符串前后空格
使用场景 当我们进行一些页面编辑时,字符串前后的空格,通常是无效的.因此需要在获取信息时,进行过滤. 比如: 输入:[空格][空格]a[空格]b[空格][空格][空格] 得到:a[空格]b 代码如下: ...
- 我眼中的Android IDE
我作为一个Android小白,首先跟Android打交道的就是它的IDE(Integrated Development Environment,集成开发环境)了. 记得刚开始时是从图书馆借了本Andr ...
- javascript键盘输入控制
获取键盘控制事件 document.onkeydown = keyDown 当浏览器读到这个语句时,无论按下键盘上的哪个键,都将呼叫KeyDown()函数. 不同浏览器的实现: Netscape Ne ...
- nginx 下 location 配置解释
当我们在使用负载均衡和反向代理的时候 我们会考到虚拟主机下面有着个配置 现在我们看一下反向代理的location 下面的配置实例: server { listen 80 ; 监听的端口号 ser ...
- json2form已改名为AForm
相信大部分程序员都接触过表单,表单是收集用户输入的不二之选,但是表单的开发又是最繁琐.最复杂的,简单地说,开发表单你需要涉及到很多知识: 布局,表单如何布局排版,看起来最清晰整洁,且符合用户体验 控件 ...
- 18.Android之SharedPreferences数据存储学习
SharedPreferences是Android中最容易理解的数据存储技术,实际上SharedPreferences处理的就是一个key-value(键值对)SharedPreferences常用来 ...
- 【BZOJ-1030】文本生成器 AC自动机 + DP
1030: [JSOI2007]文本生成器 Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 3253 Solved: 1330[Submit][Stat ...
- 最小圆覆盖(Smallest Enclosing Discs)
随机增量算法(a randomized incremental algorithm) #define sqr(x) ((x)*(x)) #define EPS 1e-4 struct P{ doubl ...
- json 数据交换格式与java
http://wiki.mbalib.com/wiki/数据交换 数据交换是指为了满足不同信息系统之间数据资源的共享需要,依据一定的原则,采取相应的技术,实现不同信息系统之间数据资源共享的过程. 数据 ...