POJ3750: 小孩报数问题+一道经典约瑟夫问题(猴子选大王)
又一次因为一个小错误,POJ上Wrong Answer了无数次。。。。。
在差不多要放弃的时候,发现了这个猥琐的不能再猥琐的bug,改完了提交就AC了,简直无语。。。。
本题wo采用模拟方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream>
2 #include<cstring>
3 #include<cstdio>
4 using namespace std;
5 struct child{
6 char name[16];
7 int id;
8 //child(string, int);
9 }cd[100];
10 void init(int n){
11 char s[16];
12 for(int i=1;i<=n;i++){
13 scanf("%s",cd[i].name);
14 cd[i].id=0;
15 }
16 }
17 int main(){
18 int n,w,s; char c;
19 scanf("%d",&n);
20 init(n);
21 scanf("%d%c%d",&w,&c,&s);
22 cd[w].id=1;
23 int pt=w;
24 int kill=0;
25 while(true){
26 int step=s%(n-kill)-1;
27 if(step<=0) step=step+n-kill;
28 /*
29 这里的取模运算是考虑到s值可能非常大,如果这样的话,模拟报数过程1。。。。s将会非常
30 耗时间。
31 至于为什么这么取模,自己算一算就明白了。
32 */
33 for(int i=1;i<=step;i++){
34 int ptr=pt%n+1;
35 while(cd[ptr].id==-1){//要跳过已经被kill的元素
36 ptr=ptr%n+1;
37 }
38 cd[ptr].id=cd[pt].id+1;
39 pt=ptr;
40 }//这里的pt指向的就是我们要删除的元素
41 cd[pt].id=-1;//将id赋值为-1,相当于删除动作
42 printf("%s\n",cd[pt].name);
43 kill++;
44 if(kill==n) break; //已经清空,跳出循环
45 int ptr=pt%n+1;
46 while(cd[ptr].id==-1){
47 ptr=ptr%n+1;
48 }
49 cd[ptr].id=1;
50 pt=ptr;
51
52 }
53 //system("pause");
54 return 0;
55 }
还有可以用双向循环链表来模拟:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream>
2 #include<algorithm>
3 #include<cstdio>
4 using namespace std;
5 struct node{
6 int id;
7 node *next,*pre;
8 node();
9 node(int);
10 };
11 node *head;
12 char name[101][20];
13 node::node(int value){
14 id=value;
15 next=pre=NULL;
16 }
17 void build(int n){
18 head=new node(1);
19 head->next=head->pre=head;
20 if(n==1) return;
21 node *p=head;
22 for(int i=2;i<=n;i++){
23 node *a=new node(i);
24 p->next=a;
25 a->pre=p;
26 p=a;
27 }
28 p->next=head;
29 head->pre=p;
30 }
31 void print(){
32 node *p=head;
33 printf("%d ",p->id);
34 p=p->next;
35 while(p!=head){
36 printf("%d ",p->id);
37 p=p->next;
38 }
39 printf("\n");
40 }
41 void joseph(int s,int k){
42 node *p=head;
43 s--;
44 while(s--) p=p->next;
45 while(p->next!=p){
46 for(int i=1;i<=k-1;i++) p=p->next;
47 printf("%s\n",name[p->id]);
48 node *front=p->pre;
49 node *rear=p->next;
50 p=rear;
51 front->next=rear;
52 rear->pre=front;
53 }
54 printf("%s\n",name[p->id]);
55 }
56 int main(){
57 int n; char c;
58 scanf("%d",&n);
59 build(n);
60 //print();
61 for(int i=1;i<=n;i++){
62 scanf("%s",name[i]);
63 }
64 int s,k;
65 scanf("%d%c%d",&s,&c,&k);
66 joseph(s,k);
67 }
猴子选大王问题:
题目描述约瑟夫问题:有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从第1号开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1开始报数。就这样,直到圈内只剩下一只猴子时,这个猴子就是猴王,编程求输入n,m后,输出最后猴王的编号。
输入每行是用空格分开的两个整数,第一个是 n, 第二个是 m ( 0 < m,n <=300)。最后一行是:
0 0
输出对于每行输入数据(最后一行除外),输出数据也是一行,即最后猴王的编号
样例输入
6 2
12 4
8 3
0 0
样例输出
5
1
7
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream>
2 #include<cstring>
3 #include<cstdio>
4 using namespace std;
5 struct child{
6 int name;
7 int id;
8 //child(string, int);
9 }cd[100];
10 void init(int n){
11 for(int i=1;i<=n;i++){
12 cd[i].name=i;
13 cd[i].id=0;
14 }
15 }
16 void solve(int n,int s){
17 init(n);
18 cd[1].id=1;
19 int pt=1;
20 int kill=0;
21 while(true){
22 int step=s%(n-kill)-1;
23 if(step<=0) step=step+n-kill;
24 /*
25 这里的取模运算是考虑到s值可能非常大,如果这样的话,模拟报数过程1。。。。s将会非常
26 耗时间。
27 至于为什么这么取模,自己算一算就明白了。
28 */
29 for(int i=1;i<=step;i++){
30 int ptr=pt%n+1;
31 while(cd[ptr].id==-1){//要跳过已经被kill的元素
32 ptr=ptr%n+1;
33 }
34 cd[ptr].id=cd[pt].id+1;
35 pt=ptr;
36 }//这里的pt指向的就是我们要删除的元素
37 cd[pt].id=-1;//将id赋值为-1,相当于删除动作
38 kill++;
39 if(kill==n){
40 printf("%d\n",cd[pt].name); break; //已经清空,跳出循环
41 }
42 int ptr=pt%n+1;
43 while(cd[ptr].id==-1){
44 ptr=ptr%n+1;
45 }
46 cd[ptr].id=1;
47 pt=ptr;
48
49 }
50 }
51 int main(){
52 int n,s;
53 while(scanf("%d%d",&n,&s)!=EOF&&n&&s){
54 solve(n,s);
55 }
56 return 0;
57 }