2021.12大二上學期資料結構結課設計
阿新 • • 發佈:2021-12-23
敢死隊問題
問題描述: 有 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;
}