敢死隊問題
問題描述: 有 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;
}