1012 -- Joseph

原题出处:1012 -- Joseph http://poj.org/problem?id=1012

参考出处:

優YoU http://user.qzone.qq.com/289065406/blog/1303803255

Eric Yang's Tech Space http://www.cnblogs.com/EricYang/archive/2009/09/04/1560478.html

阅读注意事项

本题解中仅讨论从0及1开始报数的Joseph递推公式,默认参与游戏人员的初始编号从0开始

Joseph
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 47838 Accepted: 18048

Description

The Joseph's problem is notoriously known. For those who are not familiar with the original problem: from among n people, numbered 1, 2, . . ., n, standing in circle every mth is going to be executed and only the life of the last remaining person will be saved. Joseph was smart enough to choose the position of the last remaining person, thus saving his life to give us the message about the incident. For example when n = 6 and m = 5 then the people will be executed in the order 5, 4, 6, 2, 3 and 1 will be saved. 

Suppose that there are k good guys and k bad guys. In the circle the first k are good guys and the last k bad guys. You have to determine such minimal m that all the bad guys will be executed before the first good guy. 

Input

The input file consists of separate lines containing k. The last line in the input file contains 0. You can suppose that 0 < k < 14.

Output

The output file will consist of separate lines containing m corresponding to k in the input file.

Sample Input

3
4
0

Sample Output

5
30

Source

题意:

2*k人坐成一圈,前k人为好人,后k人为坏人

从编号为0的人开始报数,报到m-1的人自动死去

问当m为何值时,可使在出现好人死亡之前,坏人全部死去

PS: 如前一轮编号为x的人死去,下一轮编号为0的人即为前一轮编号为(x+1)%(此轮初始人数)的人

如前一轮恰是最后一个人死去,下一轮循环回到开头那人编号为0


思路:

因k值较少(1~13),暴力枚举m即可

递推公式:

a[i]//第i轮死去的是当前轮编号为a[i]的人

a[0]=0

a[i]=(a[i-1]+m-1)%(2*k-i+1)

(i>1,2*k为初始参与人数,2*k-i为第i轮结束时剩余人数,2*k-i+1=2*k-(i-1)为第i-1轮结束时剩余人数即为第i轮开始时剩余人数)

"-1"可以理解为编号为x的人死去,编号大于x的所有人的编号减一

特例解释:如是挨个死去(即m=1)则可视为死去的永远是编号为1的人


推导时需注意2点:

1.

每轮都是以前一轮死去的人的下一个人作为编号1开始顺序编号的

k=2(n=4),m=7

则最初的编号如下

第一轮后3号死去,则以3号后的下一个人——4号作为下一轮的1号重新编号

2.

a[i]=(a[i-1]+m)%(2*k-i)(i>1)

----------------------------------------------------------------------------------------------------------------------------------------------------------------------

上为从0开始报数的Joseph递推公式,个人认为可辅助理解推导出从1开始报数的Joseph问题递推公式。


问题:

从0开始报数的Joseph问题:从0开始报数,报到m-1的人退出,剩下的人继续从0开始报数

从1开始报数的Joseph问题:从1开始报数,报到m的人退出,剩下的人继续从1开始报数。

胜利者的编号。


思路:

从0开始报数的Joseph问题:

第一个人(编号一定是(m-1)%n  原文m%n-1有错的样子-、-  )出列后

剩下n-1个人组成一个新约瑟夫环(以编号为k=((m-1)%n+1)%n=(m-1+1)%n=m%n的人开始):

从1开始报数的Joseph问题:

第一个人(编号一定是(m-1)%n+1)出列后

剩下n-1个人组成一个新约瑟夫环(以编号为k=(m-1+1)%n+1=m%n+1的人开始):


从0开始报数的Joseph问题:

k、k+1、k+2  ... n-2, n-1, 0, 1, 2 ... k-2且从k开始报数0

把编号转换一下:
k--> 0
k+1--> 1
k+2--> 2
...
...
k-2--> n-2


i次报数完完全全成为了i-1次报数的子问题

假如知道这个子问题的解,即若x是i-1次报数的退出者,则根据上表把x变回去刚好就是第i次报数的退出者

a[i]=(a[i-1]+m)%(n-i+1)

n为初始参与人数,n-i为第i轮结束时剩余人数,n-i+1=n-(i-1)为第i-1轮结束时剩余人数即为第i轮开始时剩余人数


从1开始报数的Joseph问题:

k、k+1、k+2  ... n-2, n-1, 0, 1, 2 ... k-2且从k开始报数1

把编号转换一下:
k--> 1
k+1--> 2
k+2--> 3
...
...
k-2--> n-1


i次报数完完全全成为了i-1次报数的子问题

假如知道这个子问题的解,即若x是i-1次报数的退出者,则根据上表把x变回去刚好就是第i次报数的退出者

a[i]=(a[i-1]-1+m)%(n-i+1)=(a[i-1]+m-1)%(n-i+1)

n为初始参与人数,n-i为第i轮结束时剩余人数,n-i+1=n-(i-1)为第i-1轮结束时剩余人数即为第i轮开始时剩余人数


令a[i]表示第i轮报数退出者的编号,结果自然是a[n]

递推公式:
a[0]=0
从0开始报数的Joseph问题:a[i]=(a[i-1]+m)%(n-i+1)

从1开始报数的Joseph问题:a[i]=(a[i-1]+m-1)%(n-i+1)

----------------------------------------------------------------------------------------------------------------------------------------------------------------------

[cpp]  view plain copy
  1. #include<stdio.h>  
  2. #include<string.h>  
  3. int main(){  
  4.     //参加人员初始编号从0开始  
  5.   
  6.     int k,i,m;  
  7.     //"there are k good guys and k bad guys",共计2*k人参加  
  8.     //i为循环变量,存储游戏当前死去人数  
  9.     //m为报数变量,存储游戏当前死亡数字  
  10.   
  11.     int joseph[15]={0},a[30];  
  12.     //joseph[i]存储k=i时满足条件"minimal m that all the bad guys will be executed before the first good guy"的结果  
  13.     //a[i]存储第i轮死去者在此轮中的编号  
  14.   
  15.     while( scanf( "%d",&k )!=EOF && k!=0 ){  
  16.   
  17.         if( joseph[k]==0 ){//所给k对应的满足条件结果m未知  
  18.   
  19.             for( i=1,a[0]=0,m=1/*WRONGANSWER.原因*/ ; i<=k ; i++ ){  
  20.             //第0轮(开始前)死去者的编号假定为0,死亡人数达到k个(坏)人时退出循环  
  21.                 a[i]=( a[i-1]+m-1 )%( 2*k-i+1 );//递推公式证明见题解  
  22.   
  23.                 if( a[i]<k ){//好人从0-->k-1编号,如果a[i]<k,即第i轮死去者在此轮中的编号小于k,是好人  
  24.                 //当前m不合题意,尝试m+1并把死亡人数重新置0  
  25.                     m++;  
  26.                     i=0;  
  27.                 }  
  28.   
  29.             }  
  30.             joseph[k]=m;  
  31.   
  32.         }  
  33.         printf( "%d\n",joseph[k] );  
  34.   
  35.     }  
  36.   
  37.     return 0;  
  38. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值