约瑟夫环问题

约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。我们注意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程。因此如果要追求效率,就要打破常规,实施一点数学策略。
x是胜出的编号,n为总人数,m为报数
第一个删除的编号是(m-1 % n,n是从0开始编号的),从下一个编号 k =(m % n)开始从新报数。
第二个删除的编号是第一个编号往后数m个,但是可能 k+m > n,所以 新的k = ( k + m ) % n;
所以 k = ( k + m ) % n 就是正向递推公式。
假设你现在知道了第n -1 的胜出者是x,怎么求n的胜出者呢
x加上m,这有可能大于n个人,所以 k = (x + m) % n
假设你现在知道了第n -2 的胜出者是x,怎么求n-1的胜出者呢
x加上m,这有可能大于n-1个人,所以 k = (x + m) % n-1
假设你现在知道了第n -3 的胜出者是x,怎么求n-2的胜出者呢
x加上m,这有可能大于n-2个人,所以 k = (x + m) % n-2
一直到n = 1,时递推结束了
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m) != EOF)
    {
        if(n==0&& m== 0)
            break;
        int i,k = 0;
        for(i = 2; i <= n; i++)
        {
            k = ( k + m) % i;
        }
        printf("%d %d %d\n",n,m,k+1);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值