2021.12大二上学期数据结构结课设计

敢死队问题
问题描述: 有 M 个敢死队员要炸掉敌人的一碉堡,谁都不想去,排长决定用轮回数数的办法来决 定哪个战士去执行任务。如果前一个战士没完成任务,则要再派一个战士上去。现给每个战 士编一个号,大家围坐成一圈,随便从某一个战士开始计数,当数到 N 时,对应的战士就 去执行任务,且此战士不再参加下一轮计数。如果此战士没完成任务,再从下一个战士开始 数数,被数到第 N 时,此战士接着去执行任务。以此类推,直到任务完成为止。 排长是不愿意去的,假设排长为 1 号,请你设计一程序,求出从第几号战士开始计数 才能让排长最后一个留下来而不去执行任务。
基本要求: (1)输入队伍人数 M 和计数 N,输出排长指定的开始序号; (2)至少采用两种不同的数据结构的方法实现。

感谢朗哥献祭“松籁响起之时”给我们小组抽到了最简单的课程设计题目hhhhhhhhhhhhhhhhhhhhh
这题放到洛谷上也就普及+顶天了
以下为思路及优化

/*
 * 感谢丁老师提供的扑克牌举例演示
 * 需求分析:
 *		输入:总人数m,报数要求n
 *		输出:满足最后只剩排长一人的一次报数中第一个报数的人的编号
 *
 *		最后只剩排长一人即最后一个前去任务的人是排长
 *		即排长前去任务后洞中无人,则该次报数为符合题意的报数
 *		若一次派遣中,排长前去任务,此后洞中还有战士,则该次报数为不合题意的报数
 * 名词注解:
 *		一次派遣:指从1报到n,报到n的人出局。
 *		一次报数:指从第一次派遣直到排长报数后前去任务
 *
 * 第一次会议记录:11.22上午9时。明确所使用两种数据结构为顺序表、链式队列。聂乐恒负责顺序表实现,杨朗负责链式结构队列实现。各自确认了数据结构解决问题过程
 * 第二次会议记录:11.29上午9:30。就如何用队列解决问题产生意见分歧。yl思路为仅改变尾指针指向头指针位置,nlh思路为头指针和尾指针均正常向后移动(按照常规队列进行处理)以下为nlh思路部分代码
Queue que;//定义队列
a[i] = i+1;//a数组存的是队员编号
int x;
for(int i=0; i<m; ++i) {//一次报数中第一个报数的人,从0~m-1遍历
	for(int j=0; j<m; ++j)//队员围成圈,从第一个报数的人开始向后延伸,队员入队
		que.enqueue(a[(i+j)%n]);
	do{//一次派遣
		for(int k=0; k<n-1; ++k)//报数,报到1~n-1,不需前去任务
			que.enqueue(que.dequeue());
		x = que.dequeue();//报数,报到n,前去任务。x为前去任务的人的编号
	}while(x!=0);//当排长前去任务时停止报数
	if(que.empty())//排长前去任务后山洞内无人,是一次符合题意的报数
		cout << i << "第一个报数" << endl;
	while(!que.empty())//队列置空
		que.dequeue();
}
 *
 * 第一次优化:Length()函数求山洞内剩余人数为遍历求得,可在类内部定义变量length,每次派遣--length
 * 优势分析:支持每次派遣之间的n不同。实际意义在于,可能某次报数过程中,某战士为增大计算难度使每次派遣报数要求n不同。Get(x)支持x改变依然实现报数要求
 */

yl链式队列代码

点击此处查看代码LinkQueue.h
#pragma once
#include <iostream>
#define nullptr NULL

using namespace std;
const int MaxSize = 100;

template<typename DataType>
struct Node
{
	DataType data = 0;												//此战士的编号
	bool completed = false;											//默认每个战士都不能完成任务
	Node<DataType>* next = nullptr;											
};

template<typename DataType>
class LinkQueue
{
public:
	LinkQueue();													//初始化空的链队列
	~LinkQueue();													//释放链队列的存储空间
	void EnQueue(DataType x, bool y);								//入队操作,将元素x和y入队
	DataType DeQueue();												//出队操作,将指定的对头元素出队
	int Length();													//返回当前队列的长度
	bool Empty();													//判断链队列是否为空
	bool LeaderSurvival();											//判断链队列中是否只剩下队长
	void Work(int M, int N);										//求解问题
private:
	Node<DataType>* front, * rear;									//声明队头指针和队尾指针
	int length = 0;													//记录当前队列的长度
};

template<typename DataType>
LinkQueue<DataType>::LinkQueue()									//函数功能:初始化空的链队列
{
	Node<DataType> *s = nullptr;
	s = new Node<DataType>;
	s->next = nullptr;
	front = rear = s;												//将队头指针和队尾指针都指向头结点s
}

template<typename DataType>
LinkQueue<DataType>::~LinkQueue()									//函数功能:释放链队列的存储空间
{
	Node<DataType>* p = front;
	while(!Empty())													//释放每一个结点的存储空间
	{
		front = front->next;
		delete p;
		p = front;													//工作指针后移
	}
}

template<typename DataType>
void LinkQueue<DataType>::EnQueue(DataType x, bool y)				//函数功能:将元素x和y入队
{
	Node<DataType>* s = nullptr;
	s = new Node<DataType>;											//申请结点s
	s->data = x;
	s->completed = y;
	s->next = nullptr;
	rear->next = s;													//将结点s插入到队尾
	rear = s;
	++length;
}

template<typename DataType>
DataType LinkQueue<DataType>::DeQueue()								//函数功能:将指定的队头元素出队
{
	DataType x;
	bool y;
	if(Empty()) throw "队列已经为空。";
	Node<DataType>* p = front->next;								//暂存队头元素
	x = p->data;
	y = p->completed;
	front->next = p->next;											//将队头元素所在结点摘链
	if(p->next == nullptr) rear = front;							//出队前队列长度为1
	delete p;
	--length;
	return x;
}

template<typename DataType>
int LinkQueue<DataType>::Length()									//函数功能:返回当前队列的长度
{
	//cout << "front->data:" << front->data << "  **  " << "rear->data:" << rear->data << endl;
	return length;
}

template<typename DataType>
bool LinkQueue<DataType>::Empty()									//函数功能:判断队列是否为空
{
	return (length == 0);
}

template<typename DataType>
bool LinkQueue<DataType>::LeaderSurvival()							//函数功能:判断队列中是否只剩下队长
{
	if ((length == 1) && (rear->data == 1))
		return true;
	return false;
}

template<typename DataType>
void LinkQueue<DataType>::Work(int M, int N)						//函数功能:求解敢死队问题
{
	int a[MaxSize], x;
	for (int i = 0; i < M; i++)                                     //讨论从0~10开始时,怎样才能使排长最后一个去执行任务
	{
		a[i] = i + 1;
		for (int j = 0; j < M; j++)                                 //将M个敢死队员入队
			EnQueue(a[(i + j) % M], false);
		do                                                          //数数并派遣队头的敢死队员去执行任务
		{
			//cout << "Testing:" << i << "  **  Length:" << Queue.Length() << "  **  ";
			for (int j = 0; j < N - 1; j++)
				EnQueue(DeQueue(), false);
			x = DeQueue();
			if (LeaderSurvival())
				cout << "排长可以指定的开始序号为:" << i + 1 << endl;
		} while (x != 1);

		while (Empty() != true)
			DeQueue();
	}
}

nlh顺序表

点击此处查看代码SeqList.h
#pragma once
#include <iostream>
using namespace std;
const int Maxn = 101;

int a[Maxn];
class SeqList {
public:
	SeqList() {//置零
		m = n = id = 0;
		for (int i = 0; i < Maxn; ++i)
			data[i] = 0;
		length = 0;
	}
	void Set(int M, int N) {
		m = M, n = N, id = 1;
		for (int i = 1; i <= m; ++i)
			data[i] = a[i] = i;
		length = m;
	}
	void Reset(int a[], int begin) {//重置顺序表内容,本次报数从begin开始
		for (int i = 1; i <= m; ++i)
			data[i] = a[i];
		id = begin;
		length = m;
	}
	int Length() {//返回目前队伍长度
		return length;
	}
	int Get(int x) {//查找从id号队员开始报数目前第x位的值
		int count = 0, i = id-1;//i=id-1是因为下面while中第一句++i
		while (count != x) {
			++i;
			if (i > m) i = 1;
			if (data[i] != 0)
				++count;
		}
//		cout << i << ' ';//输出该次派出执行任务的战士编号
		data[i] = 0;
		--length;
		id = i;
		return i;
	}
	void Work() {//问题求解,输出合法编号
		for (int i = 1; i <= m; ++i) {
			Reset(a, i);//重置数组,本次报数第一个报数的人编号为i
			while(Get(n) != 1);//执行派遣直到排长去执行任务
			if(Length() == 0)//排长去执行任务后洞中无人
				cout << "从第 " << i << " 号战士开始计数能让排长最后一个留下来" << endl;
		}
	}
private:
	int data[Maxn];//data[i] = i,即编号对应编号
	int m, n, id;//m为初始队伍人数,n为报数数字,id为下次开始报数的人的编号
	int length;//剩余人数。*第一次优化
};

主文件

点击此处查看代码main.cpp
#include <iostream>
#include "SeqList.h"
#include "LinkQueue.h"
using namespace std;

int main(void) {
	int m, n;
	int op;
	LinkQueue<int> Queue;
	SeqList list;
	do {
		cout << endl << endl;
		cout << "****************" << endl;
		cout << "请输入指令" << endl;
		cout << "0:停止程序" << endl;
		cout << "1:链式队列解决问题" << endl;
		cout << "2:顺序表解决问题" << endl;
		cout << "****************" << endl << endl;
		cin >> op;
		if (op == 0) break;

IN:		cout << "****************" << endl;
		cout << "请输入队伍总人数m和报数要求n" << endl;
		cout << "****************" << endl;
		cin >> m >> n;
		if (n < 0 || m < 0 || m > 101) {
			cout << "输入数据有误!请重新输入" << endl;
			goto IN;//重新输入
		}
		switch (op) {
			case 1:
				Queue.Work(m, n);
				break;
			case 2:
				list.Set(m, n);
				list.Work();
				break;
			case 0:
				break;
			default:
				cout << "输入错误!请重新输入" << endl;
		}
	}while (op != 0);
	return 0;
}
posted @ 2021-12-23 15:03  快乐永恒  阅读(98)  评论(0编辑  收藏  举报