您的位置:首页 > 其它

POJ 1012 Joseph

2013-09-16 22:42 267 查看
问题描述:

     假设有k个好人和k个坏人,围成圈坐着,前k个人是好人,后k个人是坏人。必须选定一个最小的m值,使得在第一个好人选出之前,所有的坏人全部死了。

输入描述:

     输入包括若干行,每一行都是一个数,即k,0<k<14。输入最后一行为0,表示输入结束。

输出描述:

     每一行输出对应于k值的m值。

 都说Joseph是一个经典,但是对于我这种以前没接触过的来说,哪算是经典啊。于是又去百度,找资料了解经典Joseph问题的解法。经典Joseph问题最简单的算法应该是数学方法:

                         f[0]=0;

                         f[i]=(f[i-1]+m)%i;

要得到f[i]的必须先计算f[i-1]的值,要得到f[i-1]的值又要先计算f[i-2]·····依此类推。于是有:

                          for(i=2;i<=n;i++)
{
s=(s+m)%i;
}

对于经典Joseph问题,只需要求出最后一个获胜者,因此数学法是相当简单的。但是,数学法不能输出淘汰的过程。

此题是Joseph的变形,要算出最小m值,只能遍历,至少现在没看见更好的方法。遍历的方法还是主要通过相应的数学公式迭代,只要算出的f(i)<k,就跳出此次循环,m增1,进行下一次循环,知道循环k轮。

递推公式:f[0]=0;

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

值得注意的是,f[i]是当前序列中要退出的人的序列号,从0开始,例如:

                   k=4   m=30

                  f[0]=0;

                  f[1]=(f[0]+30-1)%8=5;此时序列为(0,1,2,3,4,5,6,7),第5个数是5

                  f[2]=(f[1]+30-1)%7=6;此时序列为(0,1,2,3,4,6,7),第6个数是7

                  f[3]=(f[2]+30-1)%6=5;此时序列为(0,1,2,3,4,6),第5个数是6

                  ·············

在此题中,要求先排除的是后k个,因此必须根据递推出的公式算出f(i)>k,否则退出循环。

void main()
{
int k,m;
int i,s;
int buffer[15]={0};

while(scanf("%d",&k)!=EOF)
{
m=k+1;
if(k==0)
break;
while(1)
{
s=0;
if(buffer[k]>0)
{
m=buffer[k];
break;
}
for(i=1;i<=k;i++)
{
s=(s+m-1)%(2*k-i+1);

if(s<k)
{
m++;
break;
}
}
if(i==k+1)
{
buffer[k]=m;
break;
}
}
printf("%d\n",m);
}
}


在程序中需要,将已经计算的数据添加到一张表中,否则会TLE。在网上,还看见了有大牛提出,m不需要每次递增,m是k+1的整数倍或k+1的整数倍加1,因此,每次的m可以增加k+1,可以增速不少。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: