问题描述: 有k个坏人k个好人坐成一圈,前k个为好人(编号1~k),后k个为坏人(编号k+1~2k),现在从编号1开始报数,一直报到m,一轮结束后,必须要求第m个报数的人死掉,而且他要是坏人,在他死掉之后继续从他的下一个开始报数,也是从1开始报到m,第m个是坏人,拉出去毙掉,直到坏人全部死去,好人全部留下,我们需要编程找到这个值m; 解题思路: 推导时要注意2点: 第一:每轮都是以前一轮死掉的人的后一个人作为“1”开始顺序编号的 如:k=3 编号:1 2 3 4 5 6 正确答案m=5 第一轮报数后,5号被杀掉,那么以6号开始作为下一轮的“1”重新编号,然后数到4,四号被杀掉,然后是6号被杀掉,结束!! 第二:f[i]=(f[i-1]+m)%(n-i); (i>1) 这是网上一些地方给出的递推公式,对于本题而言是不正确的。因为这种递推公式针对
的是从0开始报数的Joseph,本题是从1开始报数的,必须要变形,最后就是由于本题k值有限,只有13个值,那么POJ的数据测试就极有可能重复测试每个k值的结果,为了节省总体时间,我们的程序只在第一次得到k值的时候计算m值,然后保存下来,当k值再次出现时,就直接把保存的结果输出,不再计算m。这是在服务器打表的处理。另外有了递推的程序后,我们就知道了每个k值对应的m值。此时追求0ms AC的同学可以利用递推程序的结果,再写一个程序,直接在程序里面打表 int Joseph[]={0,2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881,1245064}; <此题有参考某同学的······>#include<iostream> using namespace std; int main(void) { int Joseph[14]={0}; //打表,保存各个k值对应的m值 int k; while(cin>>k) { if(!k) break; if(Joseph[k]) { cout<<Joseph[k]<<endl; continue; } int n=2*k; int ans[30]={0}; //第i轮杀掉 对应当前轮的编号为ans[i]的人,每一轮都以报数为“1”的人开始重新编号 int m=1; //所求的最少的报数 for(int i=1;i<=k;i++) //轮数 { ans[i]=(ans[i-1]+m-1)%(n-i+1); //n-i为剩余的人数 if(ans[i]<k) //把好人杀掉了,m值不是所求 { i=0; m++; //枚举m值 } } Joseph[k]=m; cout<<m<<endl; } return 0; }
POJ之1012
最新推荐文章于 2018-07-24 15:00:17 发布