题目大意:有n个城市编号1,2,3,...,n。第1个城市开始网络会断,然后向后数m个,数到的城市会断网,然后继续数,数到n时从1接上数,之前断网的城市忽略,让你求第2个城市最后断网,m的最小值。
解题思路:约瑟夫环,枚举m,求最小m。
约瑟夫环的介绍看这个:http://blog.csdn.net/wuzhekai1985/article/details/6628491讲的很详细。
这里再复述下,自己也加深下印象。。。假如n个数环编号为0,1,2,...,n-1.
跨度为m,那么第一次删除的号码为 (m - 1) % n;
那么下一次的数的个数为n-1,假如n - 1个数环开始数的编号为k
那么做以下映射
k -----> 0
k+1 -----> 1
k+2 ------> 2
。。。。
k - 2 -------> n - 1
若n - 1个数的数环剩余的最后一个数为 x 时, 把x 逆推映射到数环是n个数的情况。
根据上面的映射,n - 1环的每个数加上k 然后对 n取余能进行逆映射。那么n个数的数环的剩余编号为 (x + k ) % n;
k 由上得为 m % n,那么(x + m % n) % n => ( x % n + (m % n) % n ) % n => ( x % n + m % n) % n => (x + m) % n;
同理,若n - 2个数的数环剩余最后一个数为y时, 开始的编号为z时,
z --------> 0
z + 1 --------> 1
z + 2 --------> 2
......
z - 2 ---------> n - 2
z = m % (n - 1)
n - 1个数的剩余编号为(y + z) % n - 1 ===========> (y + m) % n - 1;
所以可以得出递推式
f[1] = 0
for(int i = 2; i <= n; i++)
f[i] = (f[i-1] + m) % i
这里不管如何是从1就开始删除,那么n个数就成为了n - 1个数,
求n - 1个数的约瑟夫环,2编号映射成为0, 后面每个城市编号都减2
转换成为0~n-1个编号,求最小m,使得剩下最后一个是0
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 151;
int result[maxn];
int main()
{
result[0] = 0; result[1] = 0;
result[2] = 1;
for(int i = 3; i < maxn; i++)// 先把不同的n对应的m都求出来。
{
int m = 1;
while(true)
{
int s = 0;
for(int k = 2; k < i; k++)
s = (s + m) % k;
if(s == 0)
{
result[i] = m;
break;
}
m++;
}
}
int n;
while(true)
{
scanf("%d", &n);
if(0 == n)
break;
printf("%d\n", result[n]);
}
return 0;
}