【hoj】1016 Joseph's problem I

约瑟夫问题是一个很经典的问题,描述的是n的人围成一圈,每次数到第m个人就会被淘汰,之后在淘汰的人开始在数起第m个人,这样下去只带还剩下1个人为胜利者,这个题是约瑟夫问题的变形,它每次裁定的标准不再是一个恒定的m而是按照素数表中的第i次淘汰第i个人,所以我们需要求出素数表才能知道裁定的次序,也才能求出剩下的人的序号

首先,对于约瑟夫原本的问题是可以对每次淘汰使用逐个列举,将这n个人每个人都列举,没有出局的话就计1直到数到还没淘汰的第m个,但是这样下来对于n值很大的情况就会很耗时间,所以一定会有别的方式 :

设n个人是从0-(n-1)排列的则第一次淘汰就是第(m-1)%n个人,则剩下从 k = m%n;起重新数

k =>0

k+1 =>1

|

k-2 =>n-2;

这样该问题就变化为求解以上n-1个元素的子问题,设该子问题的解为:x‘,即为大的问题的解,然而我们需要恢复其在原问题中的位置序号,因为子问题的数是在原问题的基础上加了k所以原解应为 x = (x‘+k)%n;

所以可以递归 x‘ = (x‘‘+m)%n-1;

当还剩下一个时自己就是该问题的解序号为0,故f(1) = 0; f(2) = (f(1) + m)%2.。。。。。

1016 就是原问题的变型,所不同的是每一次的m都是动态的,所以我们可以先计算出每次递归时所需要的素数,因为要去掉n-1个人,所以肯定要n-1个素数,而当还剩2个人时使用的是第n-1个素数,3个人时是第n-2个素数。。。。。。n个人是第1个素数。。。这样每次都用特定的素数去取代m就可以把正确的解带回到原问题中

#include <iostream>
#include <cstring>
#define MAX 3600

using namespace std;
int num;
bool p[MAX];
int prim[MAX];
void findPrime()
{
    int i,n = 0;
    memset(prim,0,sizeof(prim));
    i = 2;
    prim[n++] = i;
    do{
        for(int j = 2;j<i;j++){
            if(i%j == 0)
                break;
            else if(j == i-1){
                prim[n++] = i;
                break;
            }
        }
        i++;
    }while(n < MAX);
    return;
}
int main()
{
    int a,i;
    int  m;
    findPrime();
    while(cin>>num){
        if(num == 0)
            break;
        int s = 0;
        for(i = 2;i <= num;i++){
            a = prim[num-i];
            s = (s+a)%i;
         }
        cout<<s+1<<endl;
    }
    return 0;
}

【hoj】1016 Joseph's problem I

时间: 2024-10-25 15:42:59

【hoj】1016 Joseph's problem I的相关文章

【hoj】 1017 Joseph&#39;s problem II

这个是约瑟夫的另一个变型,变为总共有2*k个人,先是K个好人后是k个坏人,要求前k次都要杀坏人,即在杀掉第一个好人之前就要把所有的坏人都杀光,所以需要我们求出满足这个条件的最小的m值: 由约瑟夫的递归模型可以发现,我们因为他的递归是从最后杀的人递归到原有的人数,所以我们可以吧顺序反过来,等价于最后杀掉k个坏人,再杀好人,这样在递归的时候就是先知道起始位置(先杀的人),这样就能迭代,由有好人时是否杀的是坏人来判定这个m是否适合,如果k次后杀到了第k个坏人则说明这个m是适合的 参考:http://w

【hoj】1031 背包问题

题目:http://acm.hit.edu.cn/hoj/problem/view?id=1031 刚学完算法,但是一直都只停留在ppt以及伪代码层次,都没有实际的编过程序,所以一遇到这个题就傻了,用了贪心,但是有些情况求得的不是最优解,用动态规划才能考虑到所有情况找到最优解 #include <iostream> #include <cstdio> #define MAX 510 #define MAXH 10010 using namespace std; int min(in

【hoj】2651 pie 二分查找

二分查找是一个很基本的算法,针对的是有序的数列,通过中间值的大小来判断接下来查找的是左半段还是右半段,直到中间值的大小等于要找到的数时或者中间值满足一定的条件就返回,所以当有些问题要求在一定范围内找到一个满足一些约束的值时就可以用二分查找,时间复杂度O(log n); 题目:http://acm.hit.edu.cn/hoj/problem/view?id=2651 因为题目有精度要求,对于浮点数小数点部分会有一定误差,所以可以选择将这些有小数部分的数值扩大e6倍,因为题目要求精确到e-3,之后

【hoj】1013just the facts

首先,这个题是要求出给定数字的阶乘结果的倒数第一个不为0 的尾数,这需要我们通过阶乘的性质归纳总结出一定的规律,其次,由于题目要求的数字较大,对于高精度的数据可以适当采用java来求解 原文链接http://blog.csdn.net/rappy/article/details/1903360 首先对数列 d [10] = {1, 1, 2, 3, 4, 1, 6, 7, 8, 9} 和 ff [10] = {1, 1, 2, 6, 4, 4, 4, 8, 4, 6} 有 d [0] * ...

【hoj】2608 assemble 二分法

/* 思路:本文要求找到满足预算的最好配置的组件,组装计算机,所以可以是按照 *计算机的quantity以标准去查找相应的组件,就可以应用二分法,将每一种组件中的类型都按照质量排序 *选择满足整体质量的要求的情况下的最低价格 */ #include<iostream> #include<string> #include<string.h> #include<algorithm> #include<cstdio> #include<cstri

【BZOJ3489】A simple rmq problem kd-tree

[BZOJ3489]A simple rmq problem Description 因为是OJ上的题,就简单点好了.给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大.如果找不到这样的数,则直接输出0.我会采取一些措施强制在线. Input 第一行为两个整数N,M.M是询问数,N是序列的长度(N<=100000,M<=200000) 第二行为N个整数,描述这个序列{ai},其中所有1<=ai<=N 再下面M行,每

【hoj】1604 cable master

简单,二分法,但是在oj上交的时候会有精度的限制,所以只能把数扩得大一点,而且在扩的时候为防止尾数会自动生成一些非零数,所以还是自己吧扩到的位置设置为0,还有输出时因为%.2lf会自己有4设5入,所以可以自己算出小数点后两位的数值,在输出,保证要求的精度 #include <iostream> #include <stdio.h> #include <algorithm> #define MAX 10010 using namespace std; long long

【POJ】3468 A Simple Problem with Integers ——线段树 成段更新 懒惰标记

A Simple Problem with Integers Time Limit:5000MS   Memory Limit:131072K Case Time Limit:2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each

【CF903G】Yet Another Maxflow Problem 线段树

[CF903G]Yet Another Maxflow Problem 题意:一张图分为两部分,左边有n个点A,右边有m个点B,所有Ai->Ai+1有边,所有Bi->Bi+1有边,某些Ai->Bj有边,每条边都有一定的容量. 先要求你支持两种操作: 1.修改某条Ai->Ai+1的边的容量2.询问从A1到Bm的最大流 n,m<=100000,流量<=10^9 题解:很有思维含量的题. 首先,我们把求最大流变成求最小割.容易发现,我们最多只会割一条Ai->Ai+1的边