C++ 实现对选手、评委的计分

1 篇文章 0 订阅
1 篇文章 0 订阅

为了加快运算,之前评分都采用了其他人写的 Visual Basic 程序。界面大致是这样的:

选手数量:__________  评委数量:__________

分数¹:____________________

[按钮] 输出结论  校验²:~~~~~~~~~~

选手结果:~~~~~~~~~~  评委结果:~~~~~~~~~~

¹分数:各分数中间用逗号隔开,全角逗号将自动转为半角。就像这样:1,2,3

²校验:会输出所有分数的平均分。

 

个人觉得,这种程序的普适性较好,可以实现多场景的计分;不过,它不能实现边输入边查错,最近就有连续输错好几次,单输入就耗去了很长时间的情形。鉴于笔者和同学们最近在学习 C++ 程序设计,尽管目前只能写出控制台程序(Console Application),但仍然有自己另写程序的想法。

计分规则是这样的:

评委得到纸条,按照选手编号(而非出场顺序)为选手打分。这个“分数”实际上是一种排名;所以 1 代表最好。一个评委不能为不同的选手打相同的名次;也就是说,像 12335 这样的评分是无效的。

在最终算总分的时候,需要减去一个最高分、一个最低分——抱歉笔者没能在刚开始就解释清楚。

若想了解写程序的更多动机,读者可以跳转到文末。

好了,话不多说,先上源代码:

/*
vs 编译程序做到不依赖 Visual C++ 库
https://blog.csdn.net/u010177286/article/details/41044665
dev-cpp 编译程序做到不依赖库
https://zhidao.baidu.com/question/209400460.html
二维向量,大约在 35 行
https://blog.csdn.net/yuanjilai/article/details/7321484
本程序使用 Visual Studio 2019 开发
*/
//本程序结合了多位同学的智慧,在此一并表示感谢
#include <fstream>
#include <iomanip>
#include <iostream>
#include <vector>
using namespace std;

int main() {
   system("title 心理课分数计算 by 电子信息类 2019");
   system("color 70");
   cout << "\n\n输入【选手】的数量,\n完成后按下回车。\n";
   int n, m;
   cin >> n;
   cout << "\n输入【评委】的数量,\n完成后按下回车。\n";
   cin >> m;
   system("cls");

   cout << "\n【选手】人数为 " << n << ",\n【评委】人数为 " << m
      << ",\n若有误,请重新打开程序。\n\n";
   if (n >= 10 || n <= 0) { //若选手个数超出 10 个,提醒换用程序
      if (m <= 0)
         cout << "错误:【评委】数量非法\n\n";
      cout << "错误:【选手】数量超出个位数\n若实际确实超出,敬请换用程序。";
      cout << "\n      --------";
      cout << "\n\n 换用程序:\n 输入分数时注意用逗号隔开。\n "
         "评委间分数无需按回车。\n 例如:\n 1,5,4,3,2,5,4,2,1,3\n";
      cout << "\n现可直接关闭窗口。";
      while (1) {
         getchar();
      }
   }
   else if (m <= 0) {
      cout << "错误:【评委】数量非法\n\n";
      while (1) {
         getchar();
      }
   }
   cout << "假设有 i 个选手,请按下面的样子,每输入 i "
      "个就按下【回车】。\n数字间【不需】用空格隔开。\n15342\n24153\n";
   cout << "\n----- 核对后 开始你的输入 -----\n";
   getchar();

   const int m_2 = m + 2, n_ = n, m_1 = m + 1, n_1 = n + 1;
   int a[m_2][n_] = { 0 };    // m+1 行、n 列的二维向量,其中 a[0]
   // 存放选手最低分、a[m+1] 存放选手最高分
   int pw[m_1][2] = { 0 };    //vector<vector<int>> pw(m + 1, vector<int>(2, 1)); //存储评委“得分”
   int pw_o[m_1] = { 0 };     //vector<int> pw_o(m + 1);
   int b[n_1] = { 0 };        //vector<int> b(n + 1);    //作用是检查评委打分是否有重复
   int score[n_] = { 0 };     //vector<int> score(n, 0); //各选手的分数总和
   int score_s[n_] = { 0 };   //vector<int> score_s(n);
   int score_r[n_] = { 0 };   //vector<int> score_r(n, 1); // s=sort, t=temp, p=prev, r=rank
   int score_t, score_p, left, right, mid = 0;
   // vs 里似乎不能通过 const int 实现“自定”大小的数组,故改用 vector
   int cur = 0, num = 0, wrong = 0;

   for (int i = 0; i < n; ++i) {
      a[0][i] = n;
      a[m + 1][i] = score_r[i] = 1;
      score[i] = 0;
   }
   for (int i = 0; i <= m; ++i) {
      pw[i][1] = 1;
   }
   while (1) {
      //每轮中数据的“初始化”
      ++cur;
      if (cur == m + 1) {
         break;
      }
      cout << "评委" << setw(2) << cur << '/' << m << ":";
      a[cur][n] = a[cur][n + 1] = num = wrong = 0;
      for (int i = 1; i <= n; ++i) {
         b[i] = 0;
      }
      //输入模块,不打扰用户的输入
      for (; num < n; ++num) {
         a[cur][num] = getchar() - '0';
         if (b[a[cur][num]] || a[cur][num] <= 0 || a[cur][num] > n ||
            a[cur][num] == -38) {
            wrong = 1;
            break;
         }
         else
            ++b[a[cur][num]];
      }
      if (!wrong)
         a[cur][num] = getchar() - '0';
      //对错误输入的处理
      if (a[cur][num] != -38)
         wrong = 1;
      if (wrong == 1) {
         --cur;
         cout << "  *** 输入有误 ***\n";
      }
   }


   //减去最高分、最低分
   for (int i = 0; i < n; ++i) {
      //统计;边输入边统计将在输入最后一组数据时出问题……
      for (cur = 1; cur <= m; ++cur) {
         score[i] += a[cur][i];
         if (a[0][i] > a[cur][i])
            a[0][i] = a[cur][i];
         if (a[m + 1][i] < a[cur][i])
            a[m + 1][i] = a[cur][i];
      }
   }
   for (int i = 0; i < n; ++i) {
      //相减 
      score[i] -= a[0][i];
      score[i] -= a[m + 1][i];
   }

   //score_s = score;
   for (int i = 0; i < n; ++i)
      score_s[i] = score[i];

   mid = 0;
   for (int i = 0; i < n; ++i) {
      for (int j = i + 1; j < n; ++j) {
         if (score_s[i] > score_s[j]) { // score_s 系顺序排序
            score_t = score_s[i];
            score_s[i] = score_s[j];
            score_s[j] = score_t;
         }
      }
      if (i == 0)
         score_p = score_s[i];
      else if (score_s[i] == score_p)
         score_r[i] = score_r[i - 1];
      else {
         score_r[i] = i + 1;
         score_p = score_s[i];
      }
   }

   system("cls");
   cout << "\n\n去除了 1 个最高分、1 个最低分……\n";
   cout << "\n----- 心理健康教育 【选手】榜 -----\n";
   for (int i = 0; i < n; ++i) {
      cout << "选手 " << i + 1 << ":总等级 " << setw(3) << score[i] << " 排名 ";
      //中序查找法找到对应数字以便输出排名
      left = 0, right = n - 1;
      while (left <= right) {
         mid = (left + right) / 2;
         if (score[i] == score_s[mid])
            break;
         else if (score[i] > score_s[mid])
            ++left;
         else
            --right;
      }
      b[i] = score_r[mid]; //将排名存起来,以便计算评委榜
      cout << setw(2) << score_r[mid] << '\n';
   }

   for (int i = 1; i <= m; ++i) {
      pw_o[i] = 0;
      for (int j = 0; j < n; ++j)
         pw_o[i] += ((b[j] - a[i][j]) * (b[j] - a[i][j]));
      pw[i][0] = pw_o[i];
   }

   for (int i = 1; i <= m; ++i) {
      for (int j = i + 1; j <= m; ++j)
         if (pw[i][0] > pw[j][0]) {
            score_t = pw[i][0];
            pw[i][0] = pw[j][0];
            pw[j][0] = score_t;
         }
      if (i == 1)
         score_p = pw[i][0];
      else if (score_p == pw[i][0])
         pw[i][1] = pw[i - 1][1];
      else {
         pw[i][1] = i;
         score_p = pw[i][0];
      }
   }

   cout << "\n----- 心理健康教育 【评委】榜 -----\n";
   for (int i = 1; i <= m; ++i) {
      cout << "评委" << setw(2) << i << " (";
      for (int j = 0; j < n; ++j) {
         cout << a[i][j];
      }
      cout << "):方差 " << setw(3) << pw_o[i] << " 排名 ";
      //中序查找法找到对应数字以便输出排名
      left = 1, right = m;
      while (left <= right) {
         mid = (left + right) / 2;
         if (pw_o[i] == pw[mid][0])
            break;
         else if (pw_o[i] > pw[mid][0])
            ++left;
         else
            --right;
      }
      cout << setw(2) << pw[mid][1] << '\n';
   }


   cout << "\n请在【记录好结果】后,选择“直接关窗口”或“按下回车”。\n\n【测试功能"
      "】若想做一致性检验,你可以按下回车。\n然后,可将弹窗内容复制到“换用"
      "的程序”上。";
   getchar();
   system("cd /d %temp%");
   fstream file_w("NEUQ_DX_2019_output.txt", ios::out);
   for (int i = 1; i <= m; ++i)    //评委数量为 m
      for (int j = 0; j < n; ++j) { //选手数量为 n
         if (!(i == 1 && j == 0))
            file_w << ',';
         file_w << a[i][j];
      }

   file_w.close();
   system("notepad.exe NEUQ_DX_2019_output.txt");

   return 0;
}

 

下面是大佬发现的 bug,或者程序更新日志。

  • 若有 5 个选手(n = 5),在某一评委处输入 555555 再回车,会使得后续所有正确输入都会识别成错误。定位在第 70 行左右。
​      for (; num < n; ++num) {
         a[cur][num] = getchar() - '0';
         if (b[a[cur][num]] || a[cur][num] <= 0 || a[cur][num] > n || a[cur][num] == -38)
         { //原先没有 a[cur][num] == -38 的判断条件
            wrong = 1; break; //原先没有 break
         }
         else
            ++b[a[cur][num]];
      }
      if (!wrong) //原先没有这个 if 判断
         a[cur][num] = getchar() - '0';​
  • 改用 mingw g++ 7.3.0 进行编译,解决调用 system 函数崩溃的问题;同时放弃对 32 位的支持。
  • 增添文件操作,以便用原程序做一致性检验。为测试功能,定位在代码末尾。
  • 由于对二维 vector 尚不清楚,笔者暂不能手动释放二维 vector 的内存;而执行默认析构函数的时候会出故障。已在更换编译器的前提下,弃用它;并对代码做了相应更改。
//定位在 50 行左右,下为先前定义“数组”、并赋予初值时的代码
   vector<vector<int>> a(
                       m + 2,
                       vector<int>(n)); // m+1 行、n 列的二维向量,其中 a[0]
   // 存放选手最低分、a[m+1] 存放选手最高分
   vector<vector<int>> pw(m + 1, vector<int>(2, 1)); //存储评委“得分”
   vector<int> pw_o(m + 1);
   vector<int> b(n + 1);    //作用是检查评委打分是否有重复
   vector<int> score(n, 0); //各选手的分数总和
   vector<int> score_s(n);
   vector<int> score_r(n, 1); // s=sort, t=temp, p=prev, r=rank
   int score_t, score_p, left, right, mid = 0;
   // vs 里似乎不能通过 const int 实现“自定”大小的数组,故改用 vector


//定位在 125 行左右,原先用
score_s = score; //现需逐个复制

 

下面是笔者写程序的若干感想。

  • 原本想用 system 函数实现文件的打开,但在 Visual Studio 2019 的编译下,其在笔者的 Windows 10 1909 的电脑里大概率崩溃。为支持此功能,笔者将改用 g++ 7.3.0 编译,并因此放弃对 32 位系统的支持。
  • 要定义“自定义大小”的数组,Visual Studio 似乎不允许直接用传统数组的方法;于是用上了 vector(向量)。
int a;
cin >> a;
const int b = a;
int c[b]; //在 Visual Studio 默认不被允许
  • 这段程序的技术不那么重要;值得读者借鉴的,是其较为人性化的界面——尽管写的是 Console Application。读者可以使用 Visual Studio 等工具尝试编译运行程序,体会其界面设计。

 

下面是一些 Q&A。

Q: 这还用写程序?Excel 它不香吗?

A: 确实如此;笔者也觉得用 Excel 更有逼格。无论是 Excel 还是 WPS 表格,这些程序都凝结了开发者的大量心血,汇聚各种数据统计功能于一身,不是我这个“小破程序”能比的。不过,就像上面所述,笔者和同学们正在学习 C++ 程序设计——抓住每一个练习写程序的机会,以锻炼自身的 C++ 开发能力。而且通过代码共享,同学们也可以提出改进意见,从中提升代码改进能力

另外,尽管界面不太美观——毕竟它只是个黑漆漆的“控制台”;但笔者认为,单在判断评分的用途上,其比用 Excel 有一定优势。例如,用户的输入过程得到了简化

Q: 无事献殷勤,写程序的真正动机到底在哪?

A: 根据笔者的一贯风格,这纯属无稽之谈。笔者写程序,纯粹出于帮助用户的真心。看到用户在之前的程序一遍遍输错之时,台下观众的焦急、台上主持的无奈,笔者便想到用自身的能力做点什么

有疑虑的读者也不是说犯了什么大错;毕竟,我们在未来,还是要面对相对势利的社会。不过,用阴阳怪气来冷嘲热讽,甚至冒充本人,是无法让人接受的;建议趁大学四年,好好反思这种过激行为;也欢迎前往咨询室,同专业的心理辅导员交心。

Q: 你大佬,我这菜鸡就不管了。

A: 用来自嘲也挺 OK 的——笔者在其他科目(例如高数)也是如此。

根据笔者一贯风格,分享程序绝不是 show off。这段程序其实只用了《C++ 程序设计(第 3 版)》的前 7 章内容,加上 vector 向量的应用;有兴趣的读者也可以自己动手写程序。真正懂得 C++ 的同学,将有大概率使用“类”等面向对象的概念;这点是笔者将努力学习的。

为什么一个仅仅用上前 7 章内容的程序,就引来了注目?其实笔者同至少 30% 的读者处于同一水平,但这款程序是真正落到实际用途的,并且收获了用户的良好反馈,其中就会有种成就感在里边。同学们现在正与笔者一同完成 C++ 课程设计——里边的程序几乎都是有实际用途的,相信同学们在仔细完成课程设计后,会有相同的成就感

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值