这个递推公式困扰了我好久,一直想不明白,今天终于搞明白,唉真是太菜了我…
进入正题…
约瑟夫问题描述都不说了应该都知道
假设数到k的人淘汰
1. 定义状态
用f(n)
表示n个人玩游戏最终的幸存者
2.推导公式
一步一步来
- 在推导
f(n)
时,淘汰第一个人(k)后游戏就变成了n-1
人的游戏 - 将剩下的人重新编号,那么,原来的
k+1
是不是就变成了下面的1
号(红色箭头) - 现在问题变成求
n-1
人玩游戏求最后幸存者,即求f(n-1)
- 假设!
n-1
人中的幸存者就是1
号,即假设f(n-1) = 1
- 那么重点来了!既然
n-1
人中幸存者是1,那么返回看n
中的幸存者是不是就是与1
对应的k-1
没错,n
人中的幸存者就是k-1
,这里不难理解,第一轮已经把k
淘汰了 - 现在是
f(n) = k + 1
f(n-1) = 1
, 这俩啥关系??
所以就推出来了啊,f(n)
比f(n-1)
多一个k
所以 f ( n ) = f ( n − 1 ) + k f(n) = f(n-1) + k f(n)=f(n−1)+k - 还没完事,
f(n) = f(n-1) + k
有可能大于n
所以再对n
求模(这个自己想想吧),所以 f ( n ) = ( f ( n − 1 ) + k ) % n f(n) = (f(n-1) + k) \% n f(n)=(f(n−1)+k)%n - 本讲解的下标从1开始,为保证最终得到的数大于0
所以最终结果
f(n) = (f(n - 1) + (k - 1)) % n + 1
3.代码实现
#include<iostream>
using namespace std;
int n, m, f[100];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
f[i] = (f[i - 1] + m - 1) % i + 1; //注意这里是模i
}
cout << f[n] << endl;
return 0;
}
4.例题
题目描述
你一定听说过经典“约瑟夫”问题吧?现在来组织一个皆大欢喜的新游戏:假设 n 个人站成一圈,从第 1 人开始交替的去掉游戏者,但只是暂时去掉(例如,首先去掉 2),直到最后剩下唯一的幸存者为止。幸存者选出后,所有比幸存者号码高的人每人将得到 1 块钱,并且永久性地离开,其余剩下的人将重复以上过程,比幸存者号码高的人每人将得到 1 块钱后离开。一旦经过这样的过程后,人数不再减少,最后剩下的那些人将得到 2 块钱。请计算一下组织者一共要付出多少钱?
例如,第一轮有 5 人,幸存者是 3,所以 4,5 得到 1 块钱后离开,下一轮幸存者仍然是 3,因此没有人离开,所以每人得到 2 块钱,总共要付出 2+2∗3=8 块钱。
输入
输入一个整数 n。(1≤n≤32767)
输出
一个整数,表示总共要付出多少钱。
样例输入
10
样例输出
13
数据规模与约定
时间限制:1 s
内存限制:256 M
100% 的数据保证 1≤n≤32767
#include<iostream>
using namespace std;
#define MAX_N 32767
int f[MAX_N + 5];
int main() {
int n;
cin >> n;
f[1] = 1;
for (int i = 2; i <= n; i++) {
f[i] = (f[i - 1] + 2 - 1) % i + 1;
}
int x = n;
while (f[x] - x) x = f[x];
cout << n + x << endl;
return 0;
}