(转)约瑟夫问题
2011-11-27 12:17
141 查看
大意就是给你n个人,编号为1-n。从第一个人开始报数,报到m时候,第m个人出列。然后下个人从1开始报数,逢m出列。问最后出列的人的编号。
刚开始用的是循环链表,纯模拟做法。发现当n很大时,铁定TLE。所以上网搜了下,发现有数学方法解决这个问题。
思路就是递推,发现子问题,然后由子问题的解推总问题的解。
公式就是:
f(1) = 0;
f(i) = (f(i - 1) + m) % i; (i > 1)
公式的含义我解释一下:
f(1)代表就一个人玩游戏,所以不需要踢人,第一个人就是获胜者,但为什么是0不是1本身呢?接着看。。。。。
f(i)代表剩i个人时要踢的人,f(i - 1)代表剩i - 1时踢掉的人,从这个人的下一个即(f(i - 1) + 1)开始,数m个人,但是从(f(i - 1) + 1)开始时,(f(i - 1) + 1)已经有一个人,所以下一次要踢掉的人就是(f(i - 1) + 1) + m - 1.。。这个应该可以理解。。。。那结果就是f(i - 1) + m。。因为可以把约瑟夫问题看成一个圈圈,所以取余是必要的,防止出界。加入n = 8, 那么要踢掉第10个人,其实就是踢掉 10 mod 8 = 2,第二个人。。。。
然后比较容易错的地方就是刚才那个问题,为什么f(1)不是1而是0呢?因为f(i)代表剩i个人时要踢的人,而1个人玩的时候就不需要踢人。同理,下面f(i)踢掉了一个人,这样,到最后,一共踢掉n-1个人,这个游戏就结束了,获胜者就是踢掉最后一个人之后剩下的那个。因为踢掉一个n就-1,所以最后剩2个人,踢掉一个之后需要+1 mod n(总人数)。这样,就可以求出获胜者。
结果就是:
1个人时候,结果为 f(1) + 1.
大于一个人的时候,结果为f(n) + 1;
至此,这个问题就解决了。。。。。。。。
代码如下:
view plain
#include<iostream>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
int ncase, n, m, ans;
scanf("%d", &ncase);
while(ncase--)
{
scanf("%d%d", &n, &m);
ans = 0;
for(int i = 2; i <= n; ++i)
{
ans = ( ans + m ) % i;
}
printf("%d\n", ans + 1); //易错点
}
return 0;
}
刚开始用的是循环链表,纯模拟做法。发现当n很大时,铁定TLE。所以上网搜了下,发现有数学方法解决这个问题。
思路就是递推,发现子问题,然后由子问题的解推总问题的解。
公式就是:
f(1) = 0;
f(i) = (f(i - 1) + m) % i; (i > 1)
公式的含义我解释一下:
f(1)代表就一个人玩游戏,所以不需要踢人,第一个人就是获胜者,但为什么是0不是1本身呢?接着看。。。。。
f(i)代表剩i个人时要踢的人,f(i - 1)代表剩i - 1时踢掉的人,从这个人的下一个即(f(i - 1) + 1)开始,数m个人,但是从(f(i - 1) + 1)开始时,(f(i - 1) + 1)已经有一个人,所以下一次要踢掉的人就是(f(i - 1) + 1) + m - 1.。。这个应该可以理解。。。。那结果就是f(i - 1) + m。。因为可以把约瑟夫问题看成一个圈圈,所以取余是必要的,防止出界。加入n = 8, 那么要踢掉第10个人,其实就是踢掉 10 mod 8 = 2,第二个人。。。。
然后比较容易错的地方就是刚才那个问题,为什么f(1)不是1而是0呢?因为f(i)代表剩i个人时要踢的人,而1个人玩的时候就不需要踢人。同理,下面f(i)踢掉了一个人,这样,到最后,一共踢掉n-1个人,这个游戏就结束了,获胜者就是踢掉最后一个人之后剩下的那个。因为踢掉一个n就-1,所以最后剩2个人,踢掉一个之后需要+1 mod n(总人数)。这样,就可以求出获胜者。
结果就是:
1个人时候,结果为 f(1) + 1.
大于一个人的时候,结果为f(n) + 1;
至此,这个问题就解决了。。。。。。。。
代码如下:
view plain
#include<iostream>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
int ncase, n, m, ans;
scanf("%d", &ncase);
while(ncase--)
{
scanf("%d%d", &n, &m);
ans = 0;
for(int i = 2; i <= n; ++i)
{
ans = ( ans + m ) % i;
}
printf("%d\n", ans + 1); //易错点
}
return 0;
}
相关文章推荐
- 约瑟夫问题与变形
- 约瑟夫问题
- 约瑟夫问题(链表经典)
- 6-1利用数组求解约瑟夫问题
- 单循环链表解决约瑟夫问题
- 链表解决约瑟夫问题
- 约瑟夫问题的实现
- poj 1012 Joseph (约瑟夫问题)
- 数据结构 环形单链表的约瑟夫问题
- 循环链表(约瑟夫问题的衍生问题)
- POJ 2746 约瑟夫问题 解题报告
- 约瑟夫问题(Josephus Problem) 用双向循环链表实现
- 约瑟夫问题
- java 二进制(原码 反码 补码),位运算,移位运算,约瑟夫问题
- 约瑟夫问题的链表实现
- 环形链表+约瑟夫问题实现
- 啰嗦的约瑟夫问题
- POJ_1781_In Danger_约瑟夫问题
- 桟和队列--约瑟夫问题
- 约瑟夫问题