1. 程式人生 > 其它 >2021.12大二上學期資料結構結課設計

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;
}