题目:N个人围成一圈顺序编号,从1号开始按1、2、3......顺序报数,报p者退出圈外,其余的人再从1、2、3开始报数,报p的人再退出圈外,以此类推。请按退出顺序输出每个退出人的原序号。
输入格式:
输入只有一行,包括一个整数N(1<=N<=3000)及一个整数p(1<=p<=5000)。
输出格式:
按退出顺序输出每个退出人的原序号,数据间以一个空格分隔,但行尾无空格。
输入样例:
7 3
输出样例:
3 6 2 7 5 1 4
这题在去年,我尝试使用数组进行解决最后惨得TLE,在今年,首先尝试第一种容易想到的办法,用单向循环列表进行解决。
单向循环链表解法
创建一个单向循环链表,每当遇到一个输出,将链表位置弹出,直到最后全部输出。
代码如下:
//使用单向循环链表
#include <iostream>
#include <malloc.h>
using namespace std;
typedef struct players
{
int n;//用于记录玩家的编号
players *next;
} players;
players *creatlist(int m)//功能是创建一个链表。并返回头结点的位置
{
players *p1, *p2, *head;
int i = 1;
head = p1 = (players *)malloc(sizeof(players));
p1->n = i++;
m--;//考虑到m=1的特殊情况
while (m--)
{
p2 = (players *)malloc(sizeof(players));
p2->n = i++;
p1->next = p2;
p1 = p2;
}
p1->next = head;
return head;
}
void deleteandprint(players *p, int n)//功能是删除和打印
{
int m = 1, flag = 0;//使p2的延迟只发生一次否则会出问题
players *tem1 = p, *tem2 = p;
while (tem1->next != tem1)
{
for (m = 1; m < n; m++)//使用m为退出判定条件
{
tem1 = tem1->next;
if (flag || m != n - 1)
tem2 = tem2->next;
else
flag = 1;
}
cout << tem1->n << ' ';
tem2->next = tem1->next;
free(tem1);
tem1 = tem2->next;
}
cout << tem1->n;//注意,由于判定条件,我们需要输出最后一个元素
}
int main()
{
players *p;
int m, n;
cin >> m >> n;//录入m,n
p = creatlist(m);//创建链表
deleteandprint(p, n);//完成输出
return 0;
}
顺序表解法:by 俊杰锅
算法思想:设置一个包含数组及数组长度的结构体,手动写一个动态数组,完成数组的删除元素等操作,其中,定位时采用公式:
tmp=(tmp+p-1)%La->length;
因为每次正确操作后,数组原指针实际上已经往前一步,所以用p-1,p为输入的第二位数字,表示每次退出的步长。每次使迭代器向后移动步长单位,输出后将该元素从数组中删去。
#include <stdio.h>
#include <stdlib.h>
typedef struct list
{
int num[10000];
int length;
} List;//结构体定义
void InitList(List *&L)
{
L = (List *)malloc(sizeof(List));
L->length = 0;
}//初始化链表
void CreateList(List *&L, int m)//L为指针类型,以引用的方式传入
{
int i;
for (i = 0; i < m; i++)
L->num[i] = i + 1;
L->length = m;
}//创建链表
int List_Delete(List *La, int x)
{
int tmp = La->num[x];
for (; x < La->length - 1; x++)
{
La->num[x] = La->num[x + 1];
}
La->length--;
return tmp;
}
void Quit(List *La, int p)
{
int i = 0;
int first = 1;
int tmp=0;
while (La->length > 0)
{
tmp=(tmp+p-1)%La->length;
if (first)
{
printf("%d", List_Delete(La, tmp));
first=0;
}
else
{
printf(" %d", List_Delete(La, tmp));
}
}
}
int main()
{
List *L;
int n,p;
scanf("%d %d",&n,&p);
InitList(L);
CreateList(L,n);
Quit(L,p);
return 0;
}
最简单的写法
使用STL完成顺序表操作
算法思想与顺序表类似,不过选择了已有的vector动态数组代替手写代码,大大提高效率
//使用STL解决该问题
#include <iostream>
#include <vector>
#include <iterator>
using namespace std;
vector<int> players;
int main()
{
int m, n;
cin >> m >> n;
for(int i=1;i<=m;i++)
players.push_back(i);//创建动态数组
vector<int>::iterator it;//创建迭代器
for(it=players.begin();players.size()!=1;)
{
for(int i=0;i<n-1;i++){it++;if(it==players.end())it=players.begin();}
//it的越界判断非常重要
cout<<*it<<' ';//输出
players.erase(it);//擦去it迭代器位置,该函数会重新对齐
}
if(it==players.end())it=players.begin();//it的越界判断
cout<<*it;
return 0;
}
以上算法时间复杂度均为O(M*N)
AC!