约瑟夫问题 URAL 1098


约瑟夫详解参考此篇博文

点击打开链接


思想:归纳为数学性问题。原文说的很好,还是直接Copy吧,因为搜索半天也没有找到原作者,所以无法添加引用地址了,如果这位大哥看到这里,请告知与我,小弟立刻加入引用链接:)


无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。我们注意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程。因此如果要追求效率,就要打破常规,实施一点数学策略。

为了讨论方便,先把问题稍微改变一下,并不影响原意:
问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。

我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):
  k  k+1  k+2  ... n-2, n-1, 0, 1, 2, ... k-2并且从k开始报0。
现在我们把他们的编号做一下转换:

k     --> 0
k+1   --> 1
k+2   --> 2
...
...
k-2   --> n-2
k-1   --> n-1
变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x'=(x+k)%n

如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是一个倒推问题!好了,思路出来了,下面写递推公式:

令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]

递推公式
f[1]=0;
f[i]=(f[i-1]+m)%i;  (i>1)

有了这个公式,我们要做的就是从1-n顺序算出f[i]的数值,最后结果是f[n]。因为实际生活中编号总是从1开始,我们输出f[n]+1
由于是逐级递推,不需要保存每个f[i],程序也是异常简单:

复制代码
 1  #include  < stdio.h >
 2  int  main()
 3  {
 4       int  n, m, i, s  =   0 ;
 5      printf ( " N M =  " );
 6      scanf( " %d%d " & n,  & m);
 7       for  (i  =   2 ; i  <=  n; i ++ )
 8      {
 9          s  =  (s  +  m)  %  i;
10      }
11      printf ( " \nThe winner is %d\n " , s + 1 );
12  }
复制代码
这个算法的时间复杂度为O(n),相对于模拟算法已经有了很大的提高。算n,m等于一百万,一千万的情况不是问题了。可见,适当地运用数学策略,不仅可以让编程变得简单,而且往往会成倍地提高算法执行效率。

相比之下,解法二的优越性不言而喻,同时说明数学确实很重要。



A - A
Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u

Description

Holding a collegiate programming contest is a very exhausting work. There is a well-known proverb that one fool can ask so many questions that a hundred clever men will not answer. And during a collegiate programming contest questions are asked by one hundred clever people.
The jury of the Third Urals Collegiate Programming Contest being clever enough has found a simple way to make its work easier. We have invented a simple algorithm that will help us answer ALL your numerous questions! Moreover, this algorithm guarantees that the same questions will have the same answers (this would be hardly possible, if we would undertook such a task ourselves). According to this algorithm a member of the jury starts to delete characters of the question in the following order:
  1. Starting from the first character he or she counts out N−1 characters (spaces, punctuation marks etc. are considered to be characters too) and deletes the Nth character.
  2. If a string ends the count continues from the beginning of the string.
  3. After deleting a character the count restarts from the character that would be the (N+1)-st in the previous count.
  4. If the last remaining character is a question-mark ("?") then the answer to the question is "Yes". If it is a space then the answer is "No". Any other character will lead to "No comments" answer.
You should help the jury and write a program that will do a hard work of answering your questions tomorrow. The number N is secret and will not be announced even after the end of the contest. Your program should use  N = 1999.
For example, taking a string "Is it a good question?" (its length is 22) the characters will be counted in the following way: "Is it a good question?Is it … quest" and "i" will be deleted. Then the count restarts from "on?Is it…" etc., until "s" will be left (thus the answer is "No comments", as usual).

Input

The input is a question, that is any text containing at least one character (end of line is not a character). Each character of the input (excepting the ends of lines) is a part of the question. You should read question from input.
The size of the input is not more than 30000.

Output

The answer.

Sample Input

input output
Does the jury of this programming contest use the
algorithm described in this problem to answer my questions?
Yes
At least, will anybody READ my question?
No
This is
UNFAIR!
No comments

Hint

There are no spaces in the sample inputs except for those between words in one line. Thus the first question contains 108 characters, the second contains 40 and the third contains 14.

//重写一遍
#include <iostream>
#include <cstdio>

using namespace std;
const int N = 1999;
char c[30005];  //先是这里少打了个零,re了

int main()
{
    int cnt = 1;
    char ch;
    //freopen("C:/Users/Admin/Desktop/input.txt", "r", stdin);
    while((ch = getchar()) != EOF)
    {
        if(ch == '\n') continue;
        else c[cnt++] = ch;
    }
    cnt--;
    //cout << cnt << endl;
    int s = 0;
    for(int i = 2; i <= cnt; i++)
    {
        s = (s + N) % i;
    }
    s++;
    //cout << s << "\n" << c[s] << endl;
    if(c[s] == '?') printf("Yes\n");
    else if(c[s] == ' ') printf("No\n");
    else printf("No comments\n");       //comments又少加了个s以后直接原题上复制粘贴,尽量避免错误
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值