ybt 2037:【例5.4】约瑟夫问题
ybt 1334:【例2-3】围圈报数
洛谷 P1996 约瑟夫问题
假设数组下标为1~n,循环控制变量i从1遍历到n后,再重新赋值为1,再遍历到n,重复此过程,直到满足某一条件,跳出循环。
int i = 1;
while(某种循环条件)
{
//...
i++; //或将这一段写为:i = i + 1 == n ? 1 : i + 1;
if(i == n)
i = 1;
}
如果数组下标为0~n-1,可以写为:
for(int i = 0; 某种循环条件; i = (i + 1)%n)
{
//...
}
考察:单链表、环形链表、stl list
设布尔数组,表示某号人是否已经出列。循环遍历数组,如果遇到不在列的人,就跳过,从当前位置开始找到m-1个在列的人,再找下一个在列的人,就是要出列的人。将该人编号输出,出列。重复上述过程n次,每次让一个人出列。
假设这n个人排成一队,在队头数人数,每数一个人,就让这个人出队,然后在队尾入队。数到第m个人时,输出这个人的编号,让该人出队,不再入队。重复上述过程n次。
模拟,每个人作为一个结点,依次指向下一个结点,形成单链表,最后一个结点的下一个结点设为第一个结点,形成环状链表。模拟题中过程,设一个指针指向第一个结点,指针向后移动m-1次,指向第m结点,将此时指向的结点的值输出,而后删除该结点,并指向下一个结点,如此循环n次。
#include
using namespace std;
int main()
{
bool isOut[1005] = {};//isOut[i]:第i人是否出列,初值为false,都没有出列。使用下标:0~n-1
int n, m, p = 0;//p:当前位置
cin >> n >> m;
for(int i = 1; i <= n; ++i)//一共输出n次
{
for(int j = 0; j < m-1; ++j)//找m-1个在列的人
{
while(isOut[p] == true)
p = (p+1)%n;
p = (p+1)%n;
}//此时p指向第m-1个数的下一个位置
while(isOut[p] == true)//再找下一个人,就是第m个在列的人
p = (p+1)%n;
isOut[p] = true;//此时p指向第m个人 让该人出列
cout << p+1 << ' ';//下标从0开始,人编号从1开始,从下标转为人编号,需要加1
}
return 0;
}
另第一种做法:
#include
using namespace std;
int main()
{
int n, m, a[105], p;//p:当前关注的人的下标
bool isOut[105] = {};//isOut[i]:a[i]人是否在圈外
cin >> n >> m;
for(int i = 1; i <= n; ++i)
a[i-1] = i;
p = n-1;//从n-1位置开始,每次向后找m次,找到的是第m个人
for(int j = 1; j <= n; ++j)
{
int i = 1;
while(i <= m)
{
p = (p + 1) % n;
if(isOut[a[p]])
continue;
else
i++;
}
cout << a[p] << ' ';
isOut[a[p]] = true;
}
return 0;
}
#include
using namespace std;
int main()
{
queue<int> que;
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; ++i)
que.push(i);
for(int i = 1; i <= n; ++i)
{
for(int i = 1; i <= m - 1; ++i)
{//将队头的人出队后,再入队到队尾
que.push(que.front());
que.pop();
}
cout << que.front() << ' ';//此时队头是要出列的人
que.pop();
}
return 0;
}
#include
using namespace std;
struct Node
{
int val;
int next;
};
Node node[1005];//结点池
int p;
int main()
{
int n, m;
cin >> n >> m;
int first, tail, np;//first:指向第一个结点 tail:尾指针 np:新结点地址
np = ++p;//第一个结点的位置
node[np].val = 1;
first = tail = np;
for(int i = 2; i <= n; ++i)
{//尾插法构建单链表
np = ++p;
node[np].val = i;
node[tail].next = np;
tail = np;
}
node[tail].next = first;//最后一个结点的next指向第一个结点,形成环状链表
int sel = tail, del;//判断是否要删掉sel的下一个结点,最初sel的next就是第1结点, del:待删除的结点
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m-1; ++j)//最初sel的next是第1结点。向后移动m-1次后,sel的next就是第m个结点。
sel = node[sel].next;
del = node[sel].next;//sel的下一个结点del就是应该删除的结点
node[sel].next = node[del].next;//删除结点del,删除后,sel的下一个结点,就是下面要看的m个结点中的第一个
cout << node[del].val << ' ';//输出被删除结点的值,也就是出列的人的编号
}
return 0;
}
#include
using namespace std;
int main()
{
list<int> li;
int n, m, ct = 0;
cin >> n >> m;
for(int i = 1; i <= n; ++i)
li.push_back(i);
list<int>::iterator it = li.begin();
while(li.empty() == false)
{
ct++;
if(ct == m)
{
cout << *it << ' ';
it = li.erase(it);
ct = 0;
}
else
it++;
if(it == li.end())
it = li.begin();
}
return 0;
}