當前位置:網站首頁>基於STL的演講比賽流程管理系統

基於STL的演講比賽流程管理系統

2022-01-27 11:16:44 笨苗得先飛

1 理論基礎

1.1 容器

本學習案例采用C++STL實現,所有用到的容器如圖,在使用時均需包含其頭文件。

容器 特點
vector 單端數組形式,可動態擴展
deque 雙端數組形式,頭尾端均可動態擴展
map 所有元素都以對組的形式存儲(key,value)
multimap 允許map容器中有重複的元素

1.2 算法

在本案例中用到了排序算法random_shuffle、sort,以及常用算術生成算法accumulate,在其頭文件中要加上< algorithm >和< numeric >頭文件

算法 功能
random_shuffle 隨意打亂容器中所有元素的次序
sort 對容器中的元素進行從小到大排序
accumulate 計算區間內容器的累加和

在本案例中還需要用到仿函數:采用STL裏面內建的關系仿函數:greater(),可實現容器元素自定義排序。內建函數加頭文件< functional >
謂詞:返回bool類型的仿函數為謂詞
一元謂詞:如果重載函數operator()接受一個參數,叫一元謂詞;
二元謂詞:如果重載函數operator()接受兩個參數,叫二元謂詞。

2 需求分析

  • 12個選手進行參加演講比賽,首先將這12個人(ABCDEFGHIJKL)分成兩組,每組6個人,其次將這12人打亂抽簽,使其依次進行演講;
  • 演講的成績由10個評委打分,每個人的分數都由這10個分數去掉最低和最高分取平均而得出;
  • 首先進行第一輪比賽,取每組前三名共計6人進入第二輪;
  • 其次第二輪中取前三名為冠亞季軍;
  • 每次的冠亞季軍的編號及其成績都會記錄在文件中,並可顯示在屏幕上;
  • 程序中也可删除曆史的記錄

3 代碼實現

在整個程序中添加了一個SpeechManager類和Speaker類,以下為整個程序的邏輯思路以及代碼:

3.1 界面的顯示及其功能

在運行程序時,首先即為登陸界面的顯示,在此案例中登陸界面分為四個功能:開始比賽,查看往届記錄,清空比賽記錄,退出比賽程序

void SpeechManager::show_menu()
{
    
	cout << "*************************************" << endl;
	cout << "***********歡迎參加演講比賽**********" << endl;
	cout << "***********1. 開始演講比賽***********" << endl;
	cout << "***********2. 查看往届記錄***********" << endl;
	cout << "***********3. 清空比賽記錄***********" << endl;
	cout << "***********4. 退出比賽程序***********" << endl;
	cout << "*************************************" << endl;
	cout << endl;
}

在main函數中,實現對功能的選擇。

SpeechManager sm;
	int choice = 0;//存儲用戶輸入
	while (true)
	{
    
		sm.show_menu();
		cout << "請輸入您的選擇:" << endl;
		cin >> choice;
		
		switch (choice)
		{
    
		case 1://開始比賽
			sm.startSpeech();
			break;
		case 2://查看往届比賽記錄
			sm.showRecord();
			break;
		case 3://清空比賽記錄
			sm.clearRecord();
			break;
		case 4://退出系統
			sm.exit_menu();
			break;
		default:
			system("cls");//清屏
			break;
		}
	}
	system("pause");
	return 0;

3.2 創建選手及初始化

在案例中用3個vector容器分別存儲比賽開始前的12個選手的編號、第二輪比賽的6個選手編號、最後冠亞季軍的3個選手編號,還有一個map容器用來創建<編號+姓名>的容器,並且創建變量最為比賽輪數的索引。
首先是初始化操作:

void SpeechManager::initSpeech()
{
    
	//初始化容器
	this->v1.clear();
	this->v2.clear();
	this->vVectory.clear();
	this->m.clear();
	this->m_Record.clear();
	//設置當前比賽為第一輪
	m_Index = 1;
}

其次為創建選手,其中選手類中的m_Score[0]為選手第一輪的成績;m_Score[1]為選手第二輪的成績,12名選手的編號為10001-10012:

void SpeechManager::createrSpeaker()
{
    
	string nameID = "ABCDEFGHIJKL";
	for (int i = 0; i < nameID.size(); i++)
	{
    
		string name = "姓名";
		name += nameID[i];
		Speaker sp;
		sp.m_Name = name;
		sp.m_Score[0] = 0;
		sp.m_Score[1] = 0;

		//將選手放入v1容器中
		v1.push_back(10001 + i);

		//將選手放入map容器
		m.insert(make_pair(10001+i, sp));

	}
}

3.3 開始比賽流程

比賽的流程為:

第一輪比賽:抽簽 ——比賽——顯示晋級結果
第二輪比賽:抽簽——比賽——顯示最終結果
記錄數據

void SpeechManager::startSpeech()
{
    
	//第一輪比賽
	//1、抽簽
	speechDraw();
	//2、比賽
	speechContest();
	//3、顯示晋級結果
	showScore();

	//第二輪比賽
	this->m_Index++;
	//1、抽簽
	speechDraw();
	//2、比賽
	speechContest();
	//3、顯示最終結果
	showScore();
	//4、記錄數據
	saveScore();
	  //重置比賽 ,獲取記錄
	this->initSpeech();
	this->createrSpeaker();
	this->loadRecord();
	cout << "本届比賽已完畢!" << endl;
	system("pause");
	system("cls");
}

3.3.1 抽簽

首先判斷是第幾輪比賽,獲取相對應的容器,采用random_shuffle算法將其中元素隨意排序:

void SpeechManager::speechDraw()
{
    
	cout << "第" << this->m_Index << "輪比賽正在進行中"<<endl;
	cout << "抽簽順序為:" << endl;
	cout << "--------------------------------------------------------------------------------------" << endl;

	if (this->m_Index == 1)
	{
    
	 random_shuffle(v1.begin(), v1.end());
	 for (vector<int>::iterator it = v1.begin(); it != v1.end(); it++)
	 {
    
		 cout << *it << " ";
	 }
	 cout << endl;
	}
	else
	{
    
		random_shuffle(v2.begin(), v2.end());
		for (vector<int>::iterator it = v2.begin(); it != v2.end(); it++)
		{
    
			cout << *it << " ";
		}
		cout << endl;
	}
	cout << "--------------------------------------------------------------------------------------"<<endl;
	system("pause");
}

3.3.2 比賽(重點部分)

*****此過程為案例中的核心部分*****
首先是用臨時容器獲取,若是第一輪就是v1,第二輪為v2:

vector<int>v_tem;
	if (this->m_Index == 1)
	{
    
		v_tem = v1;
	}
	else
	{
    
		v_tem = v2;
	}

其次就是遍曆這個臨時容器,將評委打分的分數放入map容器中:

	//評委打分
	for (vector<int>::iterator it = v_tem.begin(); it != v_tem.end(); it++)
	{
    
		n++;
		deque<double>d;
		//10個評委打分
		for (int i = 0; i < 10; i++)
		{
    
			double score = (rand() % 401 + 600) / 10.f; //60-100隨機一個數
			d.push_back(score);
		}
		//將分數從大到小排序
		sort(d.begin(), d.end(), greater<double>());
		d.pop_back();
		d.pop_front();
		double num = accumulate(d.begin(), d.end(), 0.f);
		double avg = num / d.size();


		//平均分放到map容器中
		this->m[*it].m_Score[m_Index - 1] = avg;

然後創建一個按照分數從大到小的multimap容器

//臨時容器 key為分數 value為選手編號 按照分數從大到小排序
	multimap<double, int, greater<double>> group;
在上一步的遍曆中加入:group.insert(make_pair(avg, *it));

最後分組,取每組的前三名進入第二輪,其中n為元素的個數,每6個為一組進入if語句:

		if (n % 6 == 0)
		{
    
			cout << "----------第" << n / 6 << "組:----------" << endl;
			for (multimap<double, int, greater<double>>::iterator it = group.begin(); it != group.end(); it++)
			{
    
				cout << "編號:" << it->second << " " << "姓名:" << this->m[it->second].m_Name << " " << "分數:" << this->m[it->second].m_Score[m_Index - 1] << endl;
			}
			int count = 0;
				//獲取每組的前三名
				for (multimap<double, int, greater<double>>::iterator it = group.begin(); it != group.end() && count < 3; it++, count++)
				{
    
					if (this->m_Index == 1)
					{
    
						v2.push_back(it->second);
					}
					else
					{
    
						vVectory.push_back(it->second);
					}
				}
			

			group.clear();
			cout << endl;
		}
		cout << "----------第" << this->m_Index << "輪比賽完畢----------" << endl;
	system("pause");

3.3.3 顯示晋級結果

通過map容器可以讀出每輪結束時的晋級選手

void SpeechManager::showScore()
{
    
	cout << "----------第" << this->m_Index << "輪比賽晋級選手:----------" << endl << endl;
	vector<int>v_temp;
	if (this->m_Index == 1)
	{
    
		v_temp = v2;
	}
	else
	{
    
		v_temp = vVectory;
	}

	for (vector<int>::iterator it = v_temp.begin(); it != v_temp.end(); it++)
	{
    
		cout << "編號:" << *it << " " << "姓名:" << this->m[*it].m_Name << " " << "分數:" << this->m[*it].m_Score[m_Index - 1] << endl;
	}

	system("pause");
	system("cls");
	this->show_menu();
}

3.3.4 保存分數

將冠亞季軍的編號和數據保存到.csv文件中,這裏創建一個布爾類型的成員變量fileIsEmpty來判斷文件是否為空。

void SpeechManager::saveScore()
{
    
	ofstream ofs;
	ofs.open("speech.csv", ios::out | ios::app);//用追加方式寫入文件

	//將每個選手數據寫入到文件中
	for (vector<int>::iterator it = vVectory.begin(); it != vVectory.end(); it++)
	{
    
		ofs << *it << "," << m[*it].m_Score[1] << ",";
	}
	ofs << endl;
	ofs.close();
	cout << "記錄已經保存!" << endl;

	//更改文件不為空
	this->fileIsEmpty = false;
}

3.3.5 加載記錄

上面將寫到文件中,但是寫到文件裏的內容還有”,“,因此下面用map<int, vector>m_Record容器存放數據

void SpeechManager::loadRecord()
{
    
	ifstream ifs;
	ifs.open("speech.csv", ios::in);

	//判斷文件是否存在
	if (!ifs.is_open())
	{
    
		this->fileIsEmpty = true;
		cout << "無這個文件" << endl;
	}


	//判斷文件是否為空
	char ch;
	ifs >> ch;
	if (ifs.eof())
	{
    
		cout << "文件為空!" << endl;
		this->fileIsEmpty = true;
		ifs.close();
		return;
	}

	//文件打開且不為空
	this->fileIsEmpty = false;
	ifs.putback(ch);//讀取的單個字符放回去
	string data;
	int index = 0;
	
	while (ifs>>data)
	{
    
		//cout << data;
		vector<string>v;
		int pos = -1;
		int start = 0;
		
		while(true)
		{
    
			pos = data.find(",", start);
			if (pos == -1)
			{
    
				break;
			//未找到
			}
			string temp = data.substr(start, pos - start); 
			v.push_back(temp);
			start = pos + 1;
		}
		this->m_Record.insert(make_pair(index, v));
		index++;
		
	}
	ifs.close();
	/*for (map<int,vector<string>>::iterator it = m_Record.begin();it!=m_Record.end() ;it++) { cout <<"第幾届: "<<it->first <<"冠軍編號:"<<it->second[0]<<"分數:"<<it->second[1]<< endl; }*/
}

3.4 查看往届記錄

利用上面加載的數據記錄,可獲得第i+1届的冠亞季軍的編號的和成績

void SpeechManager::showRecord()
{
    
	if (this->fileIsEmpty)
	{
    
		cout << "文件為空或者文件不存在!" << endl;
	}
	else
	{
    
		for (int i = 0; i < m_Record.size(); i++)
		{
    
			cout << "第" << i + 1 << "届" << "冠軍編號:" << m_Record[i][0] << " 得分: " << m_Record[i][1] << " "
				<< "亞軍編號:" << m_Record[i][2] << " 得分: " << m_Record[i][3] << " "
				<< "季軍編號:" << m_Record[i][4] << " 得分: " << m_Record[i][5] << endl;
		}
	}
	system("pause");
	system("cls");
}

3.5 清空比賽記錄

當清空比賽記錄時,要重新初始化、創建選手、加載記錄

void SpeechManager::clearRecord()
{
    
	cout << "是否確定清空文件?" << endl;
	cout << "1、是" << endl;
	cout << "2、否" << endl;
	int select = 0;
	cin >> select;
	if (select == 1)
	{
    
		ofstream ofs("speech.csv", ios::trunc);
		ofs.close();
		this->initSpeech();
		this->createrSpeaker();
		this->loadRecord();
		cout << "清空成功!" << endl;
	}
	system("pause");
	system("cls");
}

3.5 退出系統

void SpeechManager::exit_menu()
{
    
	cout << "歡迎下次使用!" << endl;
	system("pause");
	exit(0);
}

版權聲明
本文為[笨苗得先飛]所創,轉載請帶上原文鏈接,感謝
https://cht.chowdera.com/2022/01/202201271116437299.html

隨機推薦