【递推、队列、链表】约瑟夫环问题、约瑟夫问题
1.数据量较小时,可以用队列模拟
2.数据量较大时,需要使用递推公式
公式推导举例
找规律,举个例子
n = 5, m = 4, n下标从0开始,m下标从1开始
f[1] = 0;
f[2] = (f[1] + a[4]) % i;
f[3] = (f[2] + a[3]) % i;
f[4] = (f[3] + a[4]) % i;
i + j = 6 = n + 1
j = n + 1 - i;
j = m时,下一次j=1
让j在>m时映射到[1,m],一个映射小技巧:先将j减去1个偏移量,取余后再+1。
则(j - 1) % m + 1 = (n - i + 1 - 1) % m + 1 = (n - i) % m + 1
ps:如果m下标从0开始,j映射到[0,m-1]则直接模m就行,不用移位
i + j - 1 = 6 = n - 1 // j-1是因为m从0开始,a[4]的位置就是a[m-1]即a[j-1]
j = n - i, 映射到[0,m-1],则有(n - i) % m
终极版例题 1455. 招聘
n为约瑟夫环长度,m为要报的整数
n下标从1开始, m下标从1开始,1or2<=i<=n
f[1] = 1
n下标从1开始,那只有一个人时活下来的人就是1
m为定值时(此时无视m下标,只看n的下标)
f[n] = (f[n-1] + m - 1) % i + 1
m为数组给出
f[n] = (f[n-1] + a[(n - i) % m + 1] - 1) % i + 1
n下标从1开始,m下标从0开始,1or2<=i<=n
f[1] = 1
n下标从1开始,那只有一个人时活下来的人就是1
m为定值时(此时无视m下标,只看n的下标)
f[n] = (f[n-1] + m - 1) % i + 1
m为数组给出
f[n] = (f[n-1] + a[(n - i) % m] - 1) % i + 1
本题题目规定n下标从1开始,m下标可以自己定
n下标从0开始,m下标从0开始(n下标是题目规定的)
f[1] = 0
n下标从0开始,那只有一个人时活下来的人就是0
m为定值时(此时无视m下标,只看n的下标)
f[n] = (f[n-1] + m) % i
m为数组给出
f[n] = (f[n-1] + a[(n - i) % m]) % i
,1or2<=i<=n
0 <= (n - i) % m <= m - 1
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int N = 1010;
int a[N];
int n, m, T;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> T;
while (T--)
{
cin >> n >> m;
for (int i = 0; i < m; i++)
cin >> a[i];
int res = 0; //f[1] = 0
for (int i = 2; i <= n; i++)
{//f[n] = (f[n-1] + m) % i, m = a[(n - i) % m]
res = (res + a[(n - i) % m]) % i;
}
cout << res << '\n';
}
return 0;
}
n下标从0开始,m下标从1开始(n下标为题目规定)
f[1] = 0
n下标从0开始,那只有一个人时活下来的人就是0
m为定值时(此时无视m下标,只看n的下标)
f[n] = (f[n-1] + m) % i
m为数组给出
f[n] = (f[n-1] + a[(n - i) % m + 1]) % i
,1or2<=i<=n
1 <= (n - i) % m + 1 <= m
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int N = 1010;
int a[N];
int n, m, T;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> T;
while (T--)
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
cin >> a[i];
int res = 0;
for (int i = 2; i <= n; i++)
{
res = (res + a[(n - i) % m + 1]) % i;//与下标为0的区别就是+1
}
cout << res << '\n';
}
return 0;
}
P8671 [蓝桥杯 2018 国 AC] 约瑟夫环
n下标从1开始,m下标从1开始
m为定值(此时无视m下标,只看n的下标)
f[n] = (f[n-1] + m - 1) % i + 1
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n, k;
cin >> n >> k;
int res = 1;
for (int c = 1; c <= n; c++) //枚举环的长度
{
res = (res + k - 1) % c + 1;
}
cout << res;
return 0;
}
P1996 约瑟夫问题
数据范围小,可用队列模拟
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
int n, k;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> k;
queue<int> q;
for (int i = 1; i <= n; i++)
q.push(i);
int cnt = 0;
while (q.size())
{
int t = q.front();
q.pop();
cnt++;
if (cnt % k != 0)
q.push(t);
else
cout << t << ' ', cnt = 0;
}
return 0;
}
3630. 报数游戏
递推法 时间复杂度
n下标从1开始,m下标从1开始
m为定值(此时无视m下标,只看n的下标)
f[n] = (f[n-1] + m - 1) % i + 1
#include <iostream>
#include <queue>
using namespace std;
int n, k = 3;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
int res = 1;
for (int c = 1; c <= n; c++)
{
res = (res + k - 1) % c + 1;
}
cout << res;
return 0;
}
本题数量不大,可以用队列模拟
时间复杂度
#include <iostream>
#include <queue>
using namespace std;
const int N = 110;
int n;
queue<int> q;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++)
q.push(i);
int cnt = 0;
while (q.size() > 1)
{
int t = q.front();
q.pop();
cnt++;
if (cnt % 3)
q.push(t);
else
cnt = 0;//可不写
}
cout << q.front();
return 0;
}
3253. 游戏
#include <iostream>
#include <queue>
using namespace std;
int n, k;
bool check(int x)
{
if (x % k == 0 || (x % 10) == k) return true;
return false;
}
int main()
{
cin >> n >> k;
queue<int> q;
for (int i = 1; i <= n; i ++ ) q.push(i);
int j = 1; // 从1开始报数
while (q.size() > 1)
{
int t = q.front();
q.pop();
if (!check(j)) q.push(t); // 如果这个数不用出局,那就再加到队尾
j ++ ;
}
cout << q.front() << endl;
return 0;
}
3559. 围圈报数
#include <iostream>
using namespace std;
const int N = 55;
int n;
int ne[N];
int main()
{
int T;
cin >> T;
while (T -- )
{
cin >> n;
for (int i = 1; i < n; i ++ ) ne[i] = i + 1;
ne[n] = 1;
int p = n;
for (int i = 0; i < n; i ++ )
{
p = ne[ne[p]]; // 走两步
cout << ne[p] << ' ';
ne[p] = ne[ne[p]]; // 删掉一个节点
}
cout << endl;
}
return 0;
}
参考资料:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析