假设当前剩下i个人(i<=n),显然这一轮m要挂(因为总是从1开始数).经过这一轮,剩下的人是:1 2 3 ... m- 1 m + 1 ... i, 我们将从m+1开始的数映射成1, 则m+2对应2, n对应i - m, 1对应成i - m + 1 m - 1对应i - 1,那么现在的问题变成了已知i - 1个人进行循环报数m,求出去的人的序号。假设已经求出了i- 1个人循环报数下最后一个出去的人的序号X0,那么它在n个人中的序号X1=(X0+ m - 1) % n + 1, 最初的X0=1 ,反复迭代X0和X1可以求出.
http://162.105.81.212/JudgeOnline/problem?id=1781
题意:有n个人,按1~n排列成一个圈,每隔一个就剔除一人,知道剩下最后一个人为止。
列出少数的规律为:
//当n分别为: (1) (2 3) (4 5 6 7) (8 9 10 ...结果对应下面的
//构造出数列:(1) (1 3) (1 3 5 7) (1 3 5 7 9 1...规律
//每一组元素个数为:1 2 4 8...
按照规律些的代码:
网上找的资料还有别的解法:
设剩下人数为n,
若n是偶数,则一轮过后只剩下奇数位的人,有n = n / 2,原本在奇数位的数字变成(k+1) / 2;
若n是奇数,则一轮过后只剩下奇数位的人,特别的原本为第一位的也应被删除,原本第3位的变成第一位,于是有n = (n-1) / 2,原本在奇数位的数字变成(k-1) / 2;
经过有限次数后,n一定变成1,这就是最后的luck。
因此逆推上去就知道luck开始所处位置了。
递归程序:
int luck(int n)
{
if(n == 1)
return 1;
if(n&1)
return luck( (n-1)/2 ) * 2 + 1;
else
return luck( n/2 ) * 2 - 1;
}
因为每次递归n都减半,复杂度是O(log(n))
在具体数学上面的解法为:
竟然在chapter1.3就有这个问题的分析,而且还进一步指出了一个更简单的解法:
设b1b2……bk是n的二进制表示,则
luck( (b1b2……bk)2 ) = (b2……bkb1),即把n循环左移一位!
新的程序
int luck(int n)
{
int i = 1;
while(i <= n) // 该循环用于产生一个i = 2^(k+1),k是n的二进制位数
i<<=1;
return ( (n - (i>>1) )<<1) + 1;
}
pku1012
http://162.105.81.212/JudgeOnline/problem?id=1012
题意:输入k,表明有k个好人和k个坏人同时围成一个圈,前面k个人是好人,后面k个人是坏人;求全部把坏人剔除出局的整数m。
分析:因为数据很大,直接模拟肯定超时,但是要加上一个小小的记忆优化就可以了。
pku2244:
题意:1-n个城市要断网,数m个断一次,问m应该数几才能让2号城市最后断网,刚开始1号城市会断。。不管m是几。。所以转换后题意为:有n-1个城市,问m数几才能让1号城市最后断网。。和上一题差不多,改下代码就好了。
分析://这题我们这么理解由于的哥城市第一个出局,那么下一轮是从城市2开始的
//对接下来n-1个城市编号,城市2就是0,然后依次下去
//这样就转化成了n-1个人的约瑟夫问题
牛人的更简单的代码: