您的位置:首页 > 其它

HDU 1443 Joseph

2017-02-12 19:03 369 查看
题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1443

题目意思:

某些人围成一个圈,现有一个数m,从第一个人开始从1报数,报到m的人over,接着从下一个人开始又从1开始报数,报到m的人又over……现在有2*k个人围成一个圈,前k个人为好人,后k个人为坏人,从第一个好人开始报数,要找到一个最小m,使得所有坏人(后k个人)比好人(前k个人)先over。

解题思路:

1.从m=k+1开始检验是否满足(或者更精炼点从m=k/2+k+1开始),不行则加1继续检验,直到找到为止。

2.检验方法:

一种是用一个数组a[2*k+1]存储这2*k个人,以a[1],a[2]……a[2*k]存储这编号为1-2*k的人的生死状态,然后模拟报数过程,把数组全部初始化为1,每over一个人就把a[over]的值变为0,每次报数时下一个人的状态若为1则报数成功,否则编号加1直到该编号的人状态为1时则报数成功,同时若下一个的编号>2*k则又跳到1开始,这种方法对每一个m值都要重新初始化数组,且每一次报数都需要访问当前编号的人的状态值,若m较大的话还要遍历循环数组好多遍,速度十分慢,即使是打表法都不太可行(用这种方法等了快一个小时都没等出k=12的结果,就不说k=13了。。);

另外还有种就是over一个人之后,把下一个人的编号设为1,重新对剩余的人编号,这样报数就与编号是一致的,可以直接用m%sum(sum为当前剩余的总人数)(若求余为0则编号为sum)求得下一个人的编号,这种就需要求出每一次新编号后k个好人的新起始编号与新结尾编号,然后判断下一个over的人的编号是否在这新起始编号与新结尾编号之间,若在则该m值不可行,需继续加1检验,每一次的新起始编号与结尾编号可由当前over的人的编号、当前的剩余总人数及上一次的起始编号共同确定。

3.编号的时候可以从0开始编,编到2*k-1,这样用(m-1)%sum(sum为当前剩余的总人数)直接得出下一个over的人的编号,当然也可以不这样,若从1开始编多一个是否m%sum==0的条件判断就可以了。

4.这道题k范围不大,适合打表,打表有两个好处,一是节省了时间,二是由于k值<14数量算是较少,检验数据很可能有多组重复的,打表仍然可以节省时间。。

总结:

1.。。

2.巧用打表。

代码如下:

首先算出1<=k<14时的m值:

#include<cstdio>
int Joseph(int k)
{
for (int m=k/2+k+1;;m++)
{
int ok=1;
int start=0,end=k-1;
int pre_exec,sum=2*k;
for (int i=1;i<=k;i++)
{
pre_exec=(m-1)%sum;
if (start<=pre_exec && pre_exec<=end)
{
ok=0;break;
}
else
{
sum-=1;
if (pre_exec<start)start=start-pre_exec-1;
else start=start+sum-pre_exec;
end=start+k-1;
}
}
if (ok)return m;
}
}
int main()
{
for (int i=1;i<14;i++)printf("k:%d m:%d\n",i,Joseph(i));
return 0;
}
上面的代码算出来结果之后就可以打表了:

#include<cstdio>
int a[13]={
2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881,
};
int main()
{
int n;
while (scanf("%d",&n)==1&&n!=0)
printf("%d\n",a[n-1]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ACM HDU