约瑟夫问题例题小结

类型1:约瑟夫问题原题:(http://acm.hdu.edu.cn/showproblem.php?pid=2925)

  大体就是一圈人,数到m的退出,询问最后留下来的人。

  这样的题是约瑟夫系列问题的基础,普通暴力做法O(n*m),不够优秀,可以通过数学方法优化至O(n),其所用的思路是将一个规模n的问题转移成规模n-1的问题。

    公式形式推导都很简单:f(i)表示规模i的约瑟夫问题最后留下人的编号,f(i)=(f(i-1)+m)%i

类型2:规模变态版:(http://poj.org/problem?id=1781)

  类型1限制只数1,2,即每次去掉偶数项,易推导f(i)=(f(i-1)+2)%i

  然而数据时限卡O(n),通过《具体数学》上一种及其奇葩的推导,有一个O(log)解法,即f(i)=i循环右移(把二进制最高为的1放在最右面)

类型3:类型1变式:(http://poj.org/problem?id=1012)

  2×n个人,通过约瑟夫方式间隔m杀人,使后n个坏人在前n个好人之前被杀的最小m。

  引入Joseph递推公式,设有n个人,(0~n-1),间隔m, f(i) 表示当前子序列中第i轮要退出的那个人

    则 f(0)=0;

      f(i)=(f(i-1)+m-1)%(n-i+1);

  这个公式我真不知道是咋来的。。。

  反正假设我们知道这个公式,那么就可以从小大到枚举m,O(n)枚举。

类型4:模型转变

  假设不再是圈,而是一个链,每次从没有死的第一个开始计数。

  这道题我确实是结合标程与打表找规律做出来的。

  打表每一轮死亡的人,设f(i,j)表示第i轮第j个死的人,我们可以将f(i,j)按照不同j值分类,这些都比较好想,

    这道题难点在于第a个人所表示的f(i,j)=a,则f(i-1,j)=a-a/k-1,人从0计数。

  上面做完就随便怎么都能做了。

现在暂时总结这么多,总之约瑟夫问题足够神奇,也足够恶心。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 1000000
int f[MAXN];
int main()
{
//        freopen("input.txt","r",stdin);
        int n,m;
        while (scanf("%d%d",&n,&m),n+m)
        {
                f[1]=0;
                int t,x;
                for (int i=2;i<=n;i++)
                {
                        f[i]=(f[i-1]+m%i)%i;
                }
                printf("%d %d %d\n",n,m,f[n]+1);
        }
}

hdu 2925

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 99000000
const int ten[7]={1,10,100,1000,10000,100000,1000000};
struct aaa
{
        int val,id,ans;
}al[10000];
bool cmp_val(aaa a1,aaa a2)
{
        return a1.val<a2.val;
}
bool cmp_id(aaa a1,aaa a2)
{
        return a1.id<a2.id;
}
int main()
{
    //    freopen("input.txt","r",stdin);
        char str[5];
        int n=0;
        while (scanf("%s",str),str[0]+str[1]+str[3]-‘0‘*3)
        {
                int i,x;
                x=((str[0]-‘0‘)*10+str[1]-‘0‘)*ten[str[3]-‘0‘];
                al[n++].val=x;
                al[n-1].id=n-1;
        }
        sort(al,al+n,cmp_val);
        int now=0;
        int x,y,z;
        int i;
        x=0;
        while (now<n && al[now].val==1)
                al[now].ans=x+1;
        for (i=2;i<=MAXN;++i)
        {
                x=(x+2)>=i?(x+2-i):(x+2);
                while (now<n && al[now].val==i)
                        al[now++].ans=x+1;
        }
        sort(al,al+n,cmp_id);
        for (i=0;i<n;i++)
        {
                printf("%d\n",al[i].ans);
        }
}

poj 1781 O(n)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 99000000
const int ten[7]={1,10,100,1000,10000,100000,1000000};

int main()
{
        freopen("input.txt","r",stdin);
        char str[5];
        int n=0;
        int x;
        while (scanf("%s",str),str[0]+str[1]+str[3]-‘0‘*3)
        {
                x=((str[0]-‘0‘)*10+str[1]-‘0‘)*ten[str[3]-‘0‘];
                for (int i=30;i>=0;i--)
                {
                        if (x&(1<<i))
                        {
                                printf("%d\n",(((x^(1<<i))<<1)+1));
                                break;
                        }
                }
        }
}

poj 1781 O(log)

时间: 2024-10-11 12:14:42

约瑟夫问题例题小结的相关文章

约瑟夫环问题小结

一 问题描述 约瑟夫环问题的基本描述如下:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为1的人开始报数,数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又出列:依此规律重复下去,要求找到最后一个出列的人或者模拟这个过程. 二 问题解法 在解决这个问题之前,首先我们对人物进行虚拟编号,即相当于从0开始把人物重新进行编号,即用0,1,2,3,...n-1来表示人物的编号,最后返回的编号结果加上1,就是原问题的解(为什么这么做呢,下文有解释).而关于该问题的解

搜索专题小结及例题:POJ2251&amp;POJ1426&amp;POJ3087&amp;POJ2488

图的遍历也称为搜索,就是从图中某个顶点出发,沿着一些边遍历图中所有的顶点,且每个顶点仅被访问一次,遍历可采取两种不同的方式:深度优先搜索(DFS)和广度优先搜索(BFS). 1.DFS算法思想` 从顶点v出发深度遍历图G的算法 ① 访问v0顶点,置vis[v0]=1,搜索v0未被访问的邻接点w,若存在邻接点w,则dfs(w),直到到达所有邻接点都被访问过的顶点u为止,接着退回一步,看是否还有其他没有被访问的邻接点.如果有,则访问此顶点,进行前述类似的访问,如果没有,就在退回一步进行搜索,重复上述

经典例题|约瑟夫环多方法解决

本文章将用循环链表.数组.递归以及循环方法对约瑟夫环问题进行讲解.其中链表法和数组法会对过程进行模拟,递归和循环将对约瑟夫环问题进行数学剖析. 问题描述 n个人围成圈,依次编号为1.2.3.....n,从1号开始依次报数,当报到m时,报m的人退出,下一个人重新从1报起,当报到m时,报m的人退出,如此循环下去,问最后剩下的那个人的编号是多少? 链表法 建立一个循环链表,节点的数值部分存储整数1至n,将尾部节点链接到第一个节点,每次遍历m-2步,把第m-1个节的指针域指向的节点数据打印出来,然后将m

约瑟夫问题小结

前前后后做过三道关于约瑟夫问题的题了.然而今天这次考试让我认识到其实我并没有完全理解约瑟夫问题的解法. 今天花了大概一个多小时的时间彻底弄明白了(可能是我太垃圾,用的时间还是太长了),以后如果再考约瑟夫应该不会挂了吧... 约瑟夫游戏(题目大意:经典约瑟夫问题,要求输出出圈顺序,数据范围$30000$) 这道题目是初学OI那段时间写的??码风还没成型.$O(n^2)$暴扫即可. 约瑟夫问题二(题目大意:经典约瑟夫问题,要求输出获胜者编号,数据范围$1e8$) 我们将一个还剩下$n$个人的约瑟夫问

约瑟夫环小结(线段树)

题目大意: 输入初始人数编号为1~n,以及初始密码m,输出出局序列. 解法一:用线段树可解. Segtree节点存储左右区间和该区间下包含的人数. void Build(int p,int left,int right)表示从编号为p,区间为[l,r]的节点开始向下建树. int Update(int p,int id)表示从编号为p的节点开始查询在新队伍中编号为id的人出局,返回该人在最初队伍中的编号. 而temp=(temp+m-1)%seg[1].manum;语句用于求出下一个出局的人在新

我学习图像处理的小结

前一段时间,我一直在制作OpenCV基础知识的课件(<学习OpenCV3.0初级实战视频课程> http://edu.51cto.com/course/10381.html,<学习OpenCV3.0中级实战视频课程>http://edu.51cto.com/course/10712.html).因为一方面我感觉如果仅仅是有比如"答题卡识别"http://edu.51cto.com/course/8637.html,"图像拼接"http://e

C指针小结2

在上一篇对指针的小结中只是片面的说明了指针的概况,与基本含义,其实说到指针必然要提及数组,数组与指针有着密不可分的联系.但是切记数组和指针根本是两个东西,指针不能和数组画等号. 数组: 例如 int a[10];这声明的是一个由10个整形组成的数组,其中包含10个整形元素,同时数组名的值是一个指针常量,也就是数组第一个元素的地址.如果前面声明的是其他类型那么,数组名的类型就是"指向其他类型的常量指针".之前说过数组与指针不是一个东西,那么他们的区别在哪里? 1.数组具有确定数量的元素,

小结:最短路

概要: 最短路是个神奇的东西,通过三角不等式,我们可以拓展出很多最短路的延伸.而求最短路的算法一般我用三种,dijkstra.spfa.floyd,第一个用于点少边多的,第一个用于点多边少的,第三个是多源最短路. 应用: 差分约束系统.一般约束条件.最短路等. 技巧及注意: 差分约束:根据三角不等式d(v)<=d(u)+w(u, v),我们通过移项,还可以得到d(v)+w(u, v)<=d(u),而这样就足以解决一些不等式约束集了,即差分约束(在一些情况下,可以考虑最长路的三角不等式).例如[

C++ 面向对象 知识点 小结

[摘要] 面向对象主要的知识点有,面向对象的基本概念,结构和类的区分,多态及各种构造函数.要求掌握 1.面向对象的基本原则(封装.继承.多态),面向对象的基本概念(类.对象.继承).2.类和结构体在访问控制上的差异.3.成员变量在静态成员变量的4个特性.引用成员变量必须采用的初始化方式.静态常量成员变量的初始化特点以及成员变量在初始化列表初始化顺序上的特点. 4. string与stack的四个初始化函数的书写. 5. 友元函数的尺度以及实现. 6.异常的2个例题要求熟练掌握. 7. 深拷贝与浅