zoukankan      html  css  js  c++  java
  • 约瑟夫游戏

    【问题描述】

    YJC 很喜欢玩游戏,今天他决定和朋友们玩约瑟夫游戏。
    约瑟夫游戏的规则是这样的:n 个人围成一圈,从 1 号开始依次报数,当报到 m 时,
    报 1、2、…、m-1 的人出局,下一个人接着从 1 开始报,保证(n-1)是(m-1)的倍数。最后剩
    的一个人获胜。
    YJC 很想赢得游戏,但他太笨了,他想让你帮他算出自己应该站在哪个位置上。

    【输入格式】

    第一行包含两个整数 n 和 m,表示人数与数出的人数。

    【输出格式】

    输出一行,包含一个整数,表示站在几号位置上能获得胜利。

    【输入输出样例】

    joseph.in joseph.out
    10 10 10

    【数据说明】

    对于 30%的数据,满足 2≤n≤1000;
    对于 50%的数据,满足 2≤n≤1000000;
    对于 100%的数据,满足 2≤m≤n<2^63 -1 且(n-1)是(m-1)的倍数。

    【题解】

    30%:直接模拟,每次暴力删除即可。时间复杂度 O(n 2 )。

    50%:递推计算。设 F[n]表示 n 个人时最后剩下的人的编号。每
    增 加 m-1 个 人 , 答 案 向 后 移 动 m 位 。 于 是 递 推 式 为
    F[n]=F[n-m+1]%(n-m+1)+m,初始 F[1]=1。时间复杂度 O(n/m)。

    100%:容(da)易(biao)看出,只有 F[m^a +m-1]=m,其余的 F[n]
    都满足 F[n]=F[n-m+1]+m。于是设 n=m^a +(m-1) k (m^a < n ≤ m^a+1 ),那
    么 F[n]=km。时间复杂度 O(log m n)。

    这个规律很难找,不过我输n/m*m有50分是什么鬼(这说明这个规律适合大部分情况)。。。

    试着弄出规律:(据说这规律只能手算,不能证)
    这个代码看得懂一些

    tr=(n-1)/(m-1)-1;
        long long a=m;
        while(a*m<=n)
        {
            tr-=a;
            a*=m;
        }
        tr*=m;
        if(tr==0)tr=n;
        cout<<tr;

    这个程序实际上是倒着推,a代表当前人数,每乘m就是往前倒推一轮。而tr相当于要删去的(m-1)的个数,倒数第一轮删m^0个,第二轮m^1个,第三轮m^2个…到不够减的时候,幸存者即为那个余数。所以最后能求出当只有一个人时的当前轮数tr,而当前人的编号就是tr×m。
    或者换种理解:
    以n=13,m=3为例:
    第一次筛后:3,6,9,12
    同时除m:1,2,3,4
    第二轮筛后:2
    ans=2×m=6得解
    a*m<=n用来判断是否前面还有一轮。
    那个if用来特判m^2=n;
    贴上递归变递推的程序。

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #define ll unsigned long long
    #define re register
    #define il inline
    #define fp(i,a,b) for(ll i=a;i<=b;i++)
    #define fq(i,a,b) for(ll i=a;i>=b;i--)
    using namespace std;
    ll i;
    il ll gi()
    {  
      re ll x=0;
      re ll t=1;
      re char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    int main()
    {
        freopen("joseph.in","r",stdin);
        freopen("joseph.out","w",stdout);
        ll n,k;
        cin>>n>>k;
        for(i=1;i<n/k;i*=k);
        cout<<(n-i)/(k-1)*k<<endl;
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
  • 相关阅读:
    Mysql日期函数,时间函数使用的总结
    java与.net比较学习系列(7) 属性
    java与.net比较学习系列(6) 数组
    java与.net比较学习系列(5) 流程控制语句
    java与.net比较学习系列(4) 运算符和表达式
    java与.net比较学习系列(3) 基本数据类型和类型转换
    java与.net比较学习系列(2) 基础语言要素
    java与.net比较学习系列(1) 开发环境和常用调试技巧
    一个简单的数字处理
    SQLSERVER分页存储过程
  • 原文地址:https://www.cnblogs.com/yanshannan/p/7375732.html
Copyright © 2011-2022 走看看