POJ 1012 Joseph 约瑟夫-保证坏人先被清除

题意:同样的约瑟夫问题,只是现在有K个好人,K个坏人,确定一个步长,是的在第一个好人被清除之前所有坏人都被清除干净。

题解:

1.坏人被清除掉的先后顺序无关紧要,知道下一个除掉的是好人还是坏人就行了。
2.由于好人一直都是k个,每除掉一个坏人,坏人数-1,所以队列的总数每次-1但是好人一直是前k个
3.下一个被清除的人在队列中的“相对”序号为 s = (s+m-1)%rest
4.只要s>=k,那么下一个被除掉的就是坏人

5.接下来说说m的取值范围:我们考察一下只剩下k+1个人时候情况,即坏人还有一个未被处决,那么在这一轮中结束位置必定在最后一个坏人,那么开始位置在哪呢?这就需要找K+2个人的结束位置,然而K+2个人的结束位置必定是第K+2个人或者第K+1个人,这样就出现两种顺序情况:GGGG.....GGGXB 或  GGGG......GGGBX (X表示有K+2个人的那一轮退出的人)所以有K+1个人的那一轮的开始位置有两种可能即第一个位置或K+1的那个位置,限定m有两种可能:t(k+1) 或 t(k+1)+1; t>=1; 若遍历每一个m必定超时,避免超时则需要打表和限制m的范围。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

bool Josephus ( int k, int m )
{
    //处理的时候默认编号为0,1,2,···n-1;
    //每剔除一个人,就对剩下的所有人重新编一次号;
    //由于上一轮剔除了一个人,假设他的编号为t,那么所有 > t 的编号都减小 1;
    //本来下一次报数应该从s+1开始,但是由于重新编号,所以现在从s开始,即s,s+1,s+2 ···s+(m-1)
    int n = k * 2, s = 0, rest;
    for ( int i = 1; i <= k; i++ )
    {
        rest = n - i + 1;
        s = ( s + m - 1 ) % rest;
        if ( s < k ) return false;
    }
    return true;
}

int num[15] = { 0, 2, 7, 5, 30, 169, 441, 1872, 7632, 1740, 93313, 459901, 1358657, 2504881 };

int main()
{
    int k, i;
    memset(num,0,sizeof(num));
    while ( scanf("%d",&k) && k )
    {
        if ( num[k] )
        {
            printf("%d\n",num[k]); continue;
        }

        for ( i = k + 1; ; i += ( k + 1) )
        {
            if ( Josephus ( k, i ) )
            {
                num[k] = i;
                printf("%d\n",i); goto end;
            }
            else if ( Josephus ( k, i + 1 ) )
            {
                num[k] = i + 1;
                printf("%d\n",i+1); goto end;
            }
        }
        end:;
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值