约瑟夫问题
题目描述
n个人围成一圈,从编号为1的开始从1~m报数,报到m的人离开,接着再从下一个人重新报数。这样反反复复,直到只剩下一个人,求编号为多少的人留下?
输入格式
两个整数n、m。n表示总人数,m表示报数的截止上限。
输出格式
一个整数,表示剩下的人的编号。
AC代码
链表版
#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; struct node { int next;//记录当前参与者的后一位的位置 int post;//记录当前参与者的前一位的位置 }; node circle[1000]; int main() { int num,exit_num,present=1;//定义:剩余人数num,报数上限exit_num,当前位置present cout<<"Please input the number of people: "<<endl; cin>>num; cout<<"Please input the exit number: "<<endl; cin>>exit_num; //初始化链表为一个圈 for(int i=1;i<=num;i++) { circle[i].next=i+1; circle[i].post=i-1; } circle[num].next=1;//最后一位的下一个为第一位 circle[1].post=num;//第一位的上一位为最后一位 while(num>1) { for(int i=1;i<exit_num;i++)//模拟报数 present=circle[present].next; circle[circle[present].post].next=circle[present].next;//对于退出者进行删除操作:上一位的下一位为当前的下一位;下一位的上一位为当前的上一位 circle[circle[present].next].post=circle[present].post; present=circle[present].next;//从当前的下一位再一次开始报数 num--;//更新剩余人数 } cout<<present<<endl; system("pause"); return 0; }
采用链表的优势就是:可以非常出色地维护一个环形的结构,并且避免了处理跳过不参与报数的位置,而且在效率上——由于直接跳过了退出的位置——相较于一般的数组更为优秀~
不过这个是下学期才学的知识......
数组版
#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; bool circle[1000]; int main() { int num,exit_num,present=1,cnt;//定义num为初始参与人数,exit_num为报数的上限,present为当前位置,cnt为剩余人数 cout<<"Please input the number of participants: "<<endl; cin>>num; cout<<"Please input the exit number"<<endl; cin>>exit_num; cnt=num;//初始化cnt为总人数 while(cnt>1) { for(int i=1;i<=exit_num;i++)//模拟报数 { if(circle[present]==true)//若当前位置的参与者已经退出,则不计入报数,还原i值 i--; else if(i==exit_num)//若当前参与者未退出,且报数已经达到上限,则令其退出 circle[present]=true; present=(present==num)?(1):(present+1);//更新当前位置为下一位 } cnt--;//更新剩余人数 } for(int i=1;i<=num;i++)//遍历整个数组,寻找剩下的唯一参与者 if(circle[i]==false) cout<<i<<endl; system("pause"); return 0; }
数组版的代码要相对复杂一点,原因在于我们不能对数组结构中的元素进行“删除”操作,这使得我们在遍历时(也就是模拟报数过程时)要考虑已经不参与报数的位置,在更新当前位置present时也要注意模拟环状的结构。另外,由于我们无法通过present的值直接确定答案,我们在最后需要单独从1~num遍历一次数组,找到尚未被标记的位置并输出。