【递推、队列、链表】约瑟夫环问题、约瑟夫问题

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] = 1n下标从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] = 1n下标从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] = 0n下标从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] = 0n下标从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. 报数游戏

递推法 时间复杂度O(n)
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;
}

本题数量不大,可以用队列模拟
时间复杂度O(nk)

#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;
}

参考资料:

  1. https://www.bilibili.com/video/BV1Dz2eYTE7T/
  2. https://www.acwing.com/solution/content/18760/
  3. https://www.acwing.com/solution/content/26913/
posted @   Tshaxz  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
Language: HTML
点击右上角即可分享
微信分享提示