pku1781,pku1012,pku2244(约瑟夫环问题)

47 篇文章 0 订阅

假设当前剩下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个人的约瑟夫问题

 

 

牛人的更简单的代码:

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值