【算法学习笔记】54.约瑟夫问题 模拟、逆推动规 SJTU OJ 1038 二哥的约瑟夫

Description

话说二哥当年学习数据结构的时候遇到了那道猴子报数的题目,其实这就是经典的约瑟夫问题。

可是当年的二哥还是个毛头小子,只会用模拟的方法,而其他同学却使用了一些令二哥完全摸不到头脑的方法。

……二哥一怒之下改了题目……

话说当年花果山的猴子要选大王,选举办法如下:

所有猴子按1-M编号围坐一圈,二哥站在圈中心,由二哥指定一个整数Kn,

之后猴子们从1号开始按顺序报数,报到Kn的猴子退出到圈外,二哥再报出一个整数Kn+1,

然后由刚刚退出的猴子的下一只猴子再开始报数,如此循环报数,直到圈内只剩下一只猴子时,这只猴子就是大王。

由于二哥希望通过此种方法控制花果山,所以现在二哥把他制定的整数序列告诉你,希望你帮他预先算出那只猴子会成为大王。

Input Format

第一行 一个整数M,表示一共有M只猴子

第二行到第M行,每行一个整数 表示二哥即将指定的M-1个整数。这些数都大于0。

Output Format

一个整数,表示最后剩下那只猴子的编号。

Hint

对于40%的数据,M<=1000, K<=1000

对于70%的数据,M<=10000, K<=10000

对于100%的数据,M<=10000, K<=100000000

Sample Input

5
1
2
3
4

Sample Output

4

 1.链表模拟法 很简单 也能AC 优化就是对K进行取余操作但是要注意一点就是 因为我习惯了用1 2 3 来做代号 在取余时可能会出现K恰好是cnt的倍数 所以要进行单独处理 这点可以用换代号为 0 1 2...来解决模拟法代码:

#include <iostream>
using namespace std;

int front[10000+10];
int nex[10000+10];
//模拟法
void out(int x){
    nex[front[x]] = nex[x];
    front[nex[x]] = front[x];
}

int main(int argc, char const *argv[])
{
    int M;
    cin>>M;
    for (int i = 1; i <= M; ++i)
    {
        front[i] = (i==1) ? M : i-1;
        nex[i] = (i==M) ? 1 : i+1;
    }
    int cur = 1;
    int cnt = M;
    int K;
    while(1){
        cin>>K;
        K %= (cnt);
        if(K==0)
            K+=cnt;
        for (int i = 0; i < K-1; ++i)
        {
            cur = nex[cur];
        }
        out(cur);
        cur = nex[cur];
        cnt--;//出去一只猴子
        if(cnt == 1){
            cout<<cur<<endl;
            break;
        }
    }

    return 0;
}

链表模拟法

  2.数学策略 逆推法 O(n)

  我们可以发现每一次踢出一个人以后,都是一个新的约瑟夫环问题,而相邻的两个约瑟夫环问题之间有一个关系。

假设Y1---->Y2 踢出的人在Y1中的编号为s1 = K1-1 (因为从0号人开始报数), 在Y1中编号为s1+1的人在Y2里编号为0 依次类推

  假如我们已经知道了Y2中被踢出的人是s2

    则 s2与s1 可以建立关系

    s2在Y1中的编号 id = s2  +  s1 + 1  =  (s2 + K1) % num[1]

也就是说 在第二局里被踢出的人的在第一局里的编号是 在第二局里的编号+上一次的K 然后对第一局人数取余

我们知道最后一局出局的人的编号是0(只有一个人)

所以可以逆推出这个人在第一局的编号 即是答案

#include <iostream>
using namespace std;
int K[10000]={0};
int main(int argc, char const *argv[])
{
    int M;
    cin>>M;
    int ans = 0;//最后一个人 是 最后一个约瑟夫环的唯一一个人

    for (int i = 1; i <= M-1; ++i){
        cin>>K[i];
    }

    //从倒数第二个约瑟夫环开始反推 一共有M个约瑟夫环
    for (int i = 2; i <= M; ++i)
    {
        ans = (ans+K[M-i+1]) % i;//此时的上一局有i个人 数了K[M-i+1]才到ans的
        //用0标识初始位置可以有效避免取余后为0的问题
    }
    cout<<ans+1<<endl;
    return 0;
}//12 2 4 3 5 76 92 92 9 4 13 13

逆推动态规划

时间: 2024-10-06 01:58:19

【算法学习笔记】54.约瑟夫问题 模拟、逆推动规 SJTU OJ 1038 二哥的约瑟夫的相关文章

【算法学习笔记】53.单调队列的简单应用 SJTU OJ 1034 二哥的金链

1034. 二哥的金链 Description 一个阳光明媚的周末,二哥出去游山玩水,然而粗心的二哥在路上把钱包弄丢了.傍晚时分二哥来到了一家小旅店,他翻便全身的口袋也没翻着多少钱,而他身上唯一值钱的就是一条漂亮的金链.这条金链散发着奇异的光泽,据说戴上它能保佑考试门门不挂,RP++.好心的老板很同情二哥的遭遇,同意二哥用这条金链来结帐.虽然二哥很舍不得这条金链,但是他必须用它来付一晚上的房钱了. 金链是环状的,一共有 N 节,老板的要价是 K 节.随便取下其中 K 节自然没问题,然而金链上每一

【算法学习笔记】66. 模拟法 数组链表 报数优化 SJTU OJ 4010 谁最有耐心

#include <iostream> #include <algorithm> #include <vector> using namespace std; struct person { int data; int id; }; int l[1001],r[1001];//存储编号为i的左边的编号和右边的编号 int data[1001];//存储编号为i的耐心值 int n;//总人数 //初始化 void init(){ cin>>n; for (i

【算法学习笔记】74. 枚举 状态压缩 填充方案 SJTU OJ 1391 畅畅的牙签袋(改)

一开始想贪心,类似启发式搜索的感觉...后来觉得不行,而且很难写. 不如就枚举.可以通过0到2^W的中的每一个数的二进制形式来对应,第一行每个位置是否作为中心点放入十字格子的情况. 当此处为0时表示不放,1时表示放. 为什么只枚举第一行的所有情况就可以了呢. 因为第一行的情况确定之后,我们可以通过推理先改变第二行某些状态,然后再根据必须把第一行充满,可以确定第二排所有必须放十字块的位置. 生成该状态数之后,调用put函数,然后先影响下一行再通过结果来确定下一行.(这个算法的根基就是,处理每一行的

【算法学习笔记】49.暴力穷举 BFS 剪枝 SJTU OJ 1357 相邻方案

相邻方案 Description 有一个5*5的矩阵,每个元素只可能是H或者J. 我们现在需要选择7个相邻的格子使得H的数量小于J的数量.其中,拥有公共边的两个格子可以被称为相邻的格子. 对于任意一种输入的5*5矩阵,请输出满足上述条件的方案总数. Input Format 共5行,表示矩阵情况.(每一个元素只可能是H或J) Output Format 一个整数N,代表不相同方案的总数. Input Sample HHHHH JHJHJ HHHHH HJHHJ HHHHH Output Samp

【算法学习笔记】36.凸包 求最大两点距离 SJTU OJ 1244 Date A Live

Description 某助教有好多好多妹纸,其中不乏来自五道口与东川路等男子职业技术学校的.然而,遥远的距离让他不得不花费大量的时间奔波于众多城市之间.为了更好地安排自己的约会计划,他想知道最远的两只妹纸之间的距离是多少. Input Format 第一行有一个整数n,表示妹纸的数量. 接下来n行,每行两个实数x,y,表示妹纸的坐标(假定在一个平面直角坐标系上). 对于80%的数据,n<=2000 对于90%的数据,n<=10000 对于100%的数据,n<=100000 Output

【算法学习笔记】72.LCS 最大公公子序列 动态规划 SJTU OJ 1065 小M的生物实验1

非常简单的DP 如果dp[i,j]表示从0到i 和 从0到j 这两段的相似度, 那么可以知道每个dp[i,j]是由三种状态转化过来的 第一种 当dna1[i]==dna2[j]的时候 dp[i-1,j-1] +  1  长度加1 第二种  否则 从下面两个状态过来那就是 dp[i][j-1] 和 dp[i-1][j]//注意因为是顺序遍历 这两个都已经计算过 取两者最大即可. #include <iostream> #include <cstring> #include <a

八大排序算法学习笔记:插入排序(一)

插入排序     包括:直接插入排序,二分插入排序(又称折半插入排序),链表插入排序,希尔排序(又称缩小增量排序).属于稳定排序的一种(通俗地讲,就是两个相等的数不会交换位置) . 直接插入排序: 1.算法的伪代码(这样便于理解):     INSERTION-SORT (A, n)             A[1 . . n] for j ←2 to n do key ← A[ j] i ← j – 1 while i > 0 and A[i] > key do A[i+1] ← A[i]

算法学习笔记 递归之 快速幂、斐波那契矩阵加速

递归的定义 原文地址为:http://blog.csdn.net/thisinnocence 递归和迭代是编程中最为常用的基本技巧,而且递归常常比迭代更为简洁和强大.它的定义就是:直接或间接调用自身.经典问题有:幂运算.阶乘.组合数.斐波那契数列.汉诺塔等.其算法思想: 原问题可分解子问题(必要条件): 原与分解后的子问题相似(递归方程): 分解次数有限(子问题有穷): 最终问题可直接解决(递归边界): 对于递归的应用与优化,直接递归时要预估时空复杂度,以免出现用时过长或者栈溢出.优化递归就是以

EM算法学习笔记2:深入理解

文章<EM算法学习笔记1:简介>中介绍了EM算法的主要思路和流程,我们知道EM算法通过迭代的方法,最后得到最大似然问题的一个局部最优解.本文介绍标准EM算法背后的原理. 我们有样本集X,隐变量Z,模型参数θ,注意他们3个都是向量,要求解的log似然函数是lnp(X|θ),而这个log似然函数难以求解,我们假设隐变量Z已知,发现lnp(X,Z|θ) 的最大似然容易求解. 有一天,人们发现引入任意一个关于隐变量的分布q(Z),对于这个log似然函数,存在这样一个分解: lnp(X|θ)=L(q,θ