https://blog.csdn.net/a_forever_dream/article/details/100769164
取模法
n n n为问题规模(人数),每 k k k个人枪毙一个。全局次数 i \rm i i从1轮转到 n \rm n n,每次问题规模减小1。
#include <cstdio>
long long n, k, ans=0;
int main()
{
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
ans=(ans+k)%i;
printf("%d",ans+1);
}
追及法
由于除法和取模运算很耗费时间,所以我们可以减少取模次数。
在上一问中,如果
k
\rm k
k值很小,那么会有很多次枪毙轮转过程的取模都是无效的。所以我们可以将这些过程一次性完成。一次进行
p
\rm p
p次,全局次数
i
\rm i
i增加
p
\rm p
p,
a
n
s
\rm ans
ans增加
a
n
s
×
k
\rm ans\times k
ans×k
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define ll long long
ll n,k;
int main()
{
scanf("%lld %lld",&n,&k);
ll i=1,ans=0,p;
//本来i应该从0开始,但是0需要特判掉,懒得打特判,于是从1开始
while(i<n)
{
p=(i-ans)/(k-1)+((i-ans)%(k-1)!=0);//距离 除以 速度的差 得到 时间
//但是因为这里是整除,所以如果(i-ans)不是(k-1)的倍数的话,还需要多走一次,这个想想就能明白
if(i+p>n)p=n-i;//要判断是否大于n
ans+=p*k;//ans走
i+=p;//i走
ans%=i;//取模
}
printf("%lld",ans+1);//别忘了+1
}
由于我们的初衷是减少除法带来的开销,我们还可以将追及时间 p \rm p p用一种更好的方法计算出来:
< N / M > = [ ( N − 1 ) / M ] + 1 , ( 0 < M < = N , M , N ∈ Z ) <N/M>=[(N-1)/M]+1 ,\quad (0<M<=N,M,N∈Z) <N/M>=[(N−1)/M]+1,(0<M<=N,M,N∈Z)
int n=(N-1)/M +1
//在本问中:
p=(i-ans-1)/(k-1)+1;
扩展题目:明哥之问
描述
唐吉诃德多弗朗明哥率领手下击败力库一族成为了德雷斯罗萨的新国王。有一天他要处决一批力库一族的余党,此时他突发奇想制定了一条有趣的规则:将所有人从1开始编号,第一个人从1开始报数,每次数到m的人被处决,后面的人再从1开始报数…依此循环往复,直到剩下最后一人。居鲁士非常幸运地成为了那最后一人,但明哥要求他回答这批待处决的力库一族的余党至少有多少人,如果答对就可以放他一条生路。居鲁士只知道自己的编号是k,你可以帮他回答明哥的“死亡之问”吗?
关于输入
两个整数m和k,用逗号分隔。m和k的含义如上所述。
关于输出
一个整数,表示这批待处决的力库一族的余党至少有多少人。
例子输入
3,1
例子输出
1
程序
#include <stdio.h>
#define ll long long
ll m, k, n;
int main()
{
scanf("%lld,%lld", &m, &k);
ll i = 1, ans = 0, p, n = k - 1;
while (++n)
{
while (i < n)
{
p = (i - ans - 1) / (m - 1) + 1;
if (i + p > n)
p = n - i;
ans += p * m;
i += p;
ans %= i;
}
if (ans + 1 == k)
break;
}
printf("%d", n);
}
这个问题可以说明移动量较小时的取模法耗时之高。对比:
#include <stdio.h>
long long m, k, ans = 0, n;
int main()
{
scanf("%d,%d", &m, &k);
n = k-1;
int i;
while (++n)
{
ans = 0;
for (i = 1; i <= n; i++)
ans = (ans + m) % i;
if (ans + 1 == k)
break;
}
printf("%d", n);
}
/*
ProgramError: Case 6. Time out.
Error 1 in case 6.
Case 0: Time = 5ms, Memory = 264kB.
Case 1: Time = 61ms, Memory = 264kB.
Case 2: Time = 133ms, Memory = 264kB.
Case 3: Time = 3ms, Memory = 264kB.
Case 4: Time = 39ms, Memory = 264kB.
Case 5: Time = 5ms, Memory = 264kB.
Empty output file: '6.out'
*/
对比取模法:
Case 0: Time = 1ms, Memory = 264kB.
Case 1: Time = 1ms, Memory = 264kB.
Case 2: Time = 1ms, Memory = 264kB.
Case 3: Time = 1ms, Memory = 264kB.
Case 4: Time = 1ms, Memory = 264kB.
Case 5: Time = 1ms, Memory = 264kB.
Case 6: Time = 6ms, Memory = 264kB.
Case 7: Time = 6ms, Memory = 264kB.
Case 8: Time = 1ms, Memory = 264kB.
Case 9: Time = 2ms, Memory = 264kB.