http://acm.hdu.edu.cn/showproblem.php?pid=1443
Joseph
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1963 Accepted Submission(s): 1194
Problem Description
The Joseph's problem is notoriously known. For those who are not familiar with the original problem: from among n people, numbered 1, 2, . . ., n, standing in circle every mth is going to be executed and only the life of the last remaining person will be saved. Joseph was smart enough to choose the position of the last remaining person, thus saving his life to give us the message about the incident. For example when n = 6 and m = 5 then the people will be executed in the order 5, 4, 6, 2, 3 and 1 will be saved.
Suppose that there are k good guys and k bad guys. In the circle the first k are good guys and the last k bad guys. You have to determine such minimal m that all the bad guys will be executed before the first good guy.
Suppose that there are k good guys and k bad guys. In the circle the first k are good guys and the last k bad guys. You have to determine such minimal m that all the bad guys will be executed before the first good guy.
Input
The input file consists of separate lines containing k. The last line in the input file contains 0. You can suppose that 0 < k < 14.
Output
The output file will consist of separate lines containing m corresponding to k in the input file.
Sample Input
3
4
0
Sample Output
5
30
约瑟夫环:当数据不是很大,直接用链表模拟就可以过,O(n*m)的复杂度 , 及n*m < 10^6 就可以直接用链表做,但是这个题因为要枚举m 故数据太大可以是超出n的值所以没办法用链表
要是数据大的话要考虑推约瑟夫环公式,现在将n个人重新编号为0~n-1 这样求解的结果要+1
下面是使用链表存的一个例子
1 #include <cstdio> 2 #include <iostream> 3 using namespace std; 4 #define maxn 300 5 6 struct Node{ 7 int v;//存入点的编号 8 int next; 9 }nd[maxn]; 10 11 int main() 12 { 13 int n, k; 14 while(cin >> n >> k) 15 { 16 for(int i = 0; i < n; i++) 17 nd[i].v = i, nd[i].next = i+1; 18 nd[n-1].next = 0; 19 20 int cur = 0; 21 for(int i = 0; i < n-1; i++) 22 { 23 for(int j = 1; j < k-1; j++) cur = nd[cur].next; 24 int dnd = nd[cur].next;//要被删除的结点的编号 25 nd[cur].next = nd[dnd].next; 26 for(int j = cur, c = 0; c < (n-i-1); c++, j = nd[j].next) printf("%d ", nd[j].v); puts("");//输出每次剔除人后的环 27 } 28 } 29 return 0; 30 }
有第一次的去除的人是(m-1)%n 也可以写成 (m%n)-1 第二次从m%n开始计数,那么要算有n 个人每m个人去除一个,则
¡为了方便,在这里我们把这n个人的编号改为从0到n-1,第一个去掉的人总是m%n-1,剩下n-1个人,这n-1个人又组成了一个从第m%n个人开始的新的约瑟夫环问题。
1 int f(int n,int m) 2 { 3 if(n==1) return 0; 4 else return ((n%m)+f(n-1,m))%n; 5 }
但是这个题要求是前K 步都要不去除好人所以要统计每次去除的人在原先的编号是多少,
1 int f(int n , int m , int t)//n 个人 每m 个人提出,第t次去除的是编号为几的人(人的编号从0到n-1) 2 { 3 if(t==1) return (m-1)%n; 4 return (m%n+f(n-1,m,t-1))%n; 5 }
下面是ac代码
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 int f(int n , int m , int t) 5 { 6 if(t==1) return (m-1)%n; 7 else return (m%n+f(n-1,m,t-1))%n; 8 }//n个人,报数到m出列,第t轮出列的人的编号 从0到n-1编号 9 int res[15]; 10 int main() 11 { 12 int n , m , k; 13 for(int k = 1 ; k <= 13 ;k++) 14 { 15 int n = 2*k; 16 for(m = k+1 ; ; m++) 17 { 18 int flag = 1; 19 for(int i = 1 ; i <= k ; i++) 20 { 21 int cnt = f(n,m ,i); 22 if(cnt>=0&&cnt<k) 23 { 24 flag = 0; 25 break; 26 } 27 } 28 if(flag ) break ;//找到最小的了就不再搜索 29 } 30 res[k] = m; 31 } 32 while(~scanf("%d",&k),k) 33 { 34 printf("%d ",res[k]); 35 } 36 return 0; 37 }