AC代码:
#include <stdio.h> void Converse(int R[],int n,int p); void Reverse(int R[], int from, int to); int main() { int num[2000],*p=num,n,k,i,j; scanf("%d %d",&n,&k); for (i=0;i<n;i++) *p++=i;//给num数组每个元素赋初值0,1,2... p=num; for (i=0;i<n-1;i++){//i代表第(i+1)轮,有n个人则要进行n-1轮 for (j=0;j<n-i;j++){//j代表编号,第(i+1)轮共有(n-i)个人 if (j+1==((i+1)*k)%(n-i)){ //因为编号从0开始,而报数从1开始,故要j+1 //对于对(i+1)轮,被淘汰掉的人报的数是(i+1)*k //将 (i+1)*k对人数(n-i)进行取余,只要某人报的是余数则被淘汰 Converse(p,n-i,j+1); //将编号从j+1开始一直到结束 对其进行逆置 到数组的开头 goto flag;// 找到淘汰的人就跳出循环 } } flag:; } printf("%d\n",num[0]);// 输出剩下的人的编号 return 0; } /*************************************************************************************** 找到被淘汰的人的编号j,则要将编号从j+1开始一直到结束的人,将他们放到数组的开头 此时数组num可以看成两部分组成 A:编号[0]到[j] B:编号[j+1]到[n-i] 目标: num数组的组成状态由 AB → BA 类比矩阵的知识有(以下将矩阵X的逆 记为 X^(-1)) BA=(A^(-1)B^(-1)^(-1) 因此目标分解为 求A的逆以及B的逆 而函数 Reverse 即用来求逆 ****************************************************************************************/ void Converse(int R[],int n,int p) { Reverse(R,0,p-1); // 求 A 的逆 Reverse(R,p,n-1); // 求 B 的逆 Reverse(R,0,n-1); // 求 A^(-1)B^(-1)的逆 } /*一维数组的逆置 就是前后元素调换位置*/ void Reverse(int R[], int from, int to) { int i, temp; for(i=0; i<(to-from+1)/2; i++){//只需循环到一半即可,不然又会交换回来 temp=R[from+i]; R[from+i]=R[to-i]; R[to-i]=temp; } }
#include <stdio.h> void Reverse(int R[], int from, int to); int main() { int num[2000],*p=num,n,k,i,j; scanf("%d %d",&n,&k); for (i=0;i<n;i++) *p++=i; p=num; for (i=0;i<n-1;i++){ for (j=0;j<n-i;j++){ if (j+1==((i+1)*k)%(n-i)){ Reverse(p,0,j); Reverse(p,j+1,n-i-1); Reverse(p,0,n-i-1); goto flag; } } flag:; } printf("%d\n",num[0]); return 0; } void Reverse(int R[], int from, int to) { int i, temp; for(i=0; i<(to-from+1)/2; i++){ temp=R[from+i]; R[from+i]=R[to-i]; R[to-i]=temp; } }