1 /************************************************************************/ 2 /* Josephus问题——数组实现 */ 3 /************************************************************************/ 4 #include <stdio.h> 5 #include <malloc.h> 6 7 int Josephus(int times, int number, int id){ 8 int *a; 9 int i, count = 0, t = 0; 10 a = (int *)malloc(sizeof(int) * number); 11 12 for(i = 0; i < number; i++) 13 a[i] = i + 1; // 数组a用于储存每个元素的编号 14 i = id - 1; 15 16 while(count < number - 1){ 17 if(a[i] != 0) 18 t++; 19 if(t == times){ 20 t = 0; 21 count++; 22 printf("%4d", a[i]); 23 a[i] = 0; // 当该元素被剔除时,该数组元素置为0 24 } 25 i++; 26 if(i == number) 27 i = 0; 28 } 29 for(i=0;i<number;i++) 30 if(a[i]!=0) 31 { 32 printf("\n最后剩余的结点是:%4d\n",a[i]); 33 return; 34 } 35 36 } 37 38 int main(){ 39 int times, number, id; 40 printf("请输入总人数:"); 41 scanf("%d", &number); 42 printf("请输入报数周期:"); 43 scanf("%d", ×); 44 printf("请输入开始报数的编号:"); 45 scanf("%d", &id); 46 Josephus(times, number, id); 47 48 return 0; 49 } 50 51 /************************************************************************/ 52 /* 总结: 53 优点为可以得出每次被剔除的元素编号 54 缺点为内存空间占用较大,没有数学归纳法快速 */ 55 /************************************************************************/ 56 57 58 /************************************************************************/ 59 /* Josephus问题——循环链表实现 */ 60 /************************************************************************/ 61 #include <stdio.h> 62 #include <malloc.h> 63 64 typedef struct LNode 65 { 66 int data; 67 struct LNode *next; 68 }LNode,*Linkhead; 69 void Josephus(int m,int n,int k) 70 { 71 Linkhead p,r,head = NULL; 72 int i; 73 for(i = 1;i <= n;i++) 74 { 75 p = (Linkhead)malloc(sizeof(LNode));//申请一个新的链结点 76 p->data = i;//存放第i个结点的编号 77 if(head == NULL) 78 head = p; 79 else 80 r->next = p; // 因为Insert和Del操作都需要之前一个节点的地址,故用r来存储。其作用类似栈的top 81 r = p; 82 } 83 p->next = head;//至此,建立一个循环链表 84 85 p = head; 86 for(i = 1;i < k;i++) 87 { 88 r=p; 89 /*请注意,此行不是多余的,因为当k!=1,但m=1时如果没有这条语句,此时删除动作无法完成*/ 90 p=p->next; 91 } //此时p指向第1个出发结点 92 93 while(p->next != p) 94 { 95 for(i = 1;i < m;i++) 96 { 97 r = p; 98 p = p->next; 99 } //p指向第m个结点,r指向第m-1个结点 100 r->next = p->next; //删除第m个结点 101 printf("%4d",p->data); //依次输出删除结点的编号 102 free(p); //释放被删除结点的空间 103 p = r->next; //p指向新的出发结点 104 } 105 printf("\n最后剩余的结点是:%4d\n",p->data);//输出最后一个结点的编号 106 } 107 108 int main(){ 109 int times, number, id; 110 printf("请输入总人数:"); 111 scanf("%d", &number); 112 printf("请输入报数周期:"); 113 scanf("%d", ×); 114 printf("请输入开始报数的编号:"); 115 scanf("%d", &id); 116 Josephus(times, number, id); 117 118 return 0; 119 } 120 121 /************************************************************************/ 122 /* 总结: 123 优点为可以得出每次被剔除的元素编号 124 缺点为相较数组方法需要更多的计算量 125 总体而言与数组方法相差无几 */ 126 /************************************************************************/ 127 128 /************************************************************************/ 129 /* Josephus问题——数学归纳法直接计算 */ 130 /************************************************************************/ 131 #include <stdio.h> 132 int main() { 133 int answer = 0; 134 int times, number, i, id; // number为环内总元素个数,times为报数周期, id为从第几个元素开始报数 135 printf("请分别输入总人数和循环次数:"); 136 scanf("%d %d", &number, ×); 137 printf("起始报号者的编号:"); 138 scanf("%d", &id); 139 for(i = 1; i <= number; i++) { 140 answer = (answer + times) % i; // 核心算法,利用数学归纳法得出 141 } 142 if(answer + id == number) 143 printf("Survial: %d\n", number); // 防止当幸存者为最后一个编号时输出0的情况 144 else 145 printf("Survival: %d\n",(answer + id) % number); 146 // 这边利用number对answer进行取余操作以防止编号数值超过最大编号(溢出) 147 148 return 0; 149 } 150 151 /************************************************************************/ 152 /* 数学方法:总结归纳法。 153 在一个n长的环里取m的步长,那么这个环里便缺少了一个元素。 154 剩下的n-1个元素构成了n-1环。而这里的元素和n长的元素之间的映射关系是: 155 Index(n) = (Index(n - 1) + m) % n。 156 而如果我们载往下一步移除元素呢,他们之间的关系则是: 157 Index(n - 1) = (Index(n - 2) + m) % (n - 1)。故不难发现其规律,那么 158 按照刚才的过程,我们这样一直移除元素下去,肯定能够找到最后一个被移除的元素。 159 这个元素则对应只有一个元素的环,很显然,它的值为0。也就是Index(1) = 0。 160 对于这个元素的索引,它对应两个元素的索引是多少呢? 161 按照前面的过程,我们倒推回去就是了。Index(2) = (Index(1) + m) % 2。 162 那么对应3个,4个元素的呢?我们这样一路继续下去就可以找到对应到n个元素的索引了。 163 所以,我们发现了一个有意思的数学归纳关系: 164 f(1) = 0, f(n) = (f(n - 1) + m) % n。 165 按照这个关系,我们可以得到最后一个被取出来的元素对应到n个元素的环里的索引值。 166 167 总结: 168 优点:利用了数学公式推导,算法较为巧妙,计算量和内存占用量相较于其他 169 方法有了很大的优化。 170 缺点:仅仅是针对最终的结果进行计算,故无法得出中间过程 */ 171 /************************************************************************/
时间: 2024-10-13 22:28:52