POJ 1012 Joseph

题意:有k个好人和k个坏人进行约瑟夫环问题,好人在前面,坏人在后面(即好人编号为0...k - 1),求一个最小的m,使他们用m报数时所有坏人在有好人出局之前出局

 

解法:一开始没怎么细想就写了个模拟……果断T了……于是想把结果打表……结果发现k = 13时根本跑不完……

于是还是枚举m,推导每次出局的人,并在线打表。

以n = 6, m = 5举例:

一开始序列为0, 1, 2, 3, 4, 5

第m%n个人出局,对应的编号为(m + 1) % n

从出局后面的人开始报数,则队列变为:

5, 0, 1, 2, 3重新编号则变为:

0, 1, 2, 3, 4

此时人数n减少了1

则第m%(n - 1)个人出局,对应的编号为(m + 1) % (n - 1)

设上一轮出局的人编号为x,本轮编号从上一轮出局的人的后一人开始,所以反推回去本轮出局的(m + 1) % (n - 1)在上一轮的编号为(x + (m + 1) % (n - 1)) % (n - 1)

化简得到(x + m + 1) % (n - 1)

这是对于第二轮来说的,每轮的人数都会减少,第k轮的时候人数为n - k + 1

所以最终的通项公式为f[0] = 0, f[i] = (f[i - 1] + m + 1) % (n - k + 1),f[i]的含义为第i轮出局的人的编号

那么只要判断前k轮有没有好人出局就可以了,一旦有好人出局则继续枚举m,没有好人出局则为答案

 

代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string>
#include<string.h>
#include<math.h>
#include<limits.h>
#include<time.h>
#include<stdlib.h>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define LL long long
using namespace std;
int main()
{
    int jos[14] = {0};//在线打表,避免反复求解
    int n;
    while(~scanf("%d", &n) && n)//n即为上面说的k,2 * n为上面说的n(变量名起的这么奇幻真的好么)
    {
        if(jos[n])
        {
            printf("%d\n", jos[n]);
            continue;
        }
        int m = 1;
        while(1)
        {
            int f = 0;
            int i = 1;
            for(; i <= n; i++)
            {
                f = (f + m - 1) % (2 * n - i + 1);
                if(f < n)
                {
                    m++;
                    break;
                }
            }
            if(i > n)
            {
                jos[n] = m;
                printf("%d\n", m);
                break;
            }
        }
    }
    return 0;
}

  

posted @ 2015-04-25 11:23  露儿大人  阅读(149)  评论(0编辑  收藏  举报