当前位置:   article > 正文

24点卡牌游戏(C++)_卡牌游戏||q3299c++

卡牌游戏||q3299c++

一  题目

24点游戏是经典的纸牌益智游戏。
mfc是一个很优秀的同学,他学习认真,经常刷题,偶尔也会打打游戏来放松omfc最喜欢卡牌类型游戏24点。
游戏的规则是∶你会被分配抽取N张扑克牌,分别从A-K,其中我们规定牌面为A的牌,其数值为1点﹔牌面为J、Q、K的牌,其数值分别为11、12、13点:数字牌的点数与其所表示的数字一致。现在你可以通过移动这N张牌的任意次序。并在两两之间插入四种运算符号+、一、*、/ (注∶除法在这里仅考虑整除的情况,例如3/2=1,4/2=2)。经过上述操作后,我们按照运算顺序计算表达式的结果,如果结果恰好凑成24点,则游戏取得胜利。
现在,你被要求和卡牌大师mfc进行一局游戏,因此你需要实现一个程序来帮助你解决如下问题:
当你读入N张牌和其对应的数值,你有多少种方法能够恰好凑出24点,并且要输出每种方法的具体情况。我们规定:两种方法不同,当且仅当对应的两个表达式字符串不同。
例如:给出的5张牌为1、2、 3①、 3②、5,其中有两个数值3的牌,为了加以区分,我们暂时用下标来进行区分,但在实际测试数据中不会有任何区分。此时,方案1+2 + 3①* 3+5和方案1+2* 3②*3①+5是完全相同的解,在结果中只需要输出一次。

输入:

4

10

输出:

6+10/2+13

6+13+10/2

10/2+6+13

10/2+13+6

13+6+10/2

13+10/2+6

6

二  分析 

  1. 将读取的数放入vector容器中,并从小到大进行排序。
  2. 借助next_permutation()产生所有数的全排列,并对该全排列依次进行操作3,4操作。
  3. 在每两个数之间插入运算符(借助多层循环或递归并利用枚举列出运算符插入顺序的所有可能)。
  4. 将插入运算符后的完整中缀表达式转成逆波兰表达式进行计算,如果计算结果为24则记录该中缀表达式。

三  源代码

预编译、全局变量和函数声明

  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4. #include<string>
  5. #include<stack>
  6. using namespace std;
  7. int N;//存放卡牌数
  8. vector<char>op = { '+','-','*','/' };
  9. vector<char>operation;//存放当前运算符
  10. //将当前顺序的操作数和运算符转成中缀表达式,若该表达式的计算结果为24则返回该表达式否则返回空串
  11. string cal(vector<int>& nums, vector<char>operation);
  12. vector<string> dfs(vector<int>& nums);//产生当前排列的所有顺序
  13. bool isNumber(string token) {//判断是否为数字
  14. return !(token == "+" || token == "-" || token == "*" || token == "/");
  15. }
  16. int InfixExpressionEvaluation(vector<string>& exception);计算当前顺序的操作数和运算符的结果

3.1  主函数体

  1. int main(){
  2. cin >> N;
  3. vector<int> nums(N, 0);
  4. for (int i = 0; i < N; i++) {
  5. cin >> nums[i];
  6. }
  7. sort(nums.begin(), nums.end());
  8. vector<string> finres;
  9. do {
  10. vector<string> res = dfs(nums);
  11. if (!res.empty()){
  12. for (string str : res)
  13. finres.push_back(str);
  14. }
  15. } while (next_permutation(nums.begin(), nums.end()));
  16. for (auto str : finres) {
  17. cout << str << endl;
  18. }
  19. cout << finres.size() << endl;
  20. system("pause");
  21. return 0;
  22. }

3.2  将每次顺序的转中缀表达式

  1. string cal(vector<int>& nums, vector<char>operation){
  2. vector<string> res;
  3. res.push_back(to_string(nums[0]));
  4. for (int i = 0, j = 1; i < operation.size(); i++, j++){
  5. switch (operation[i])
  6. {
  7. case '+':
  8. res.push_back("+");
  9. res.push_back(to_string(nums[j]));
  10. break;
  11. case '-':
  12. res.push_back("-");
  13. res.push_back(to_string(nums[j]));
  14. break;
  15. case '*':
  16. res.push_back("*");
  17. res.push_back(to_string(nums[j]));
  18. break;
  19. case '/':
  20. res.push_back("/");
  21. res.push_back(to_string(nums[j]));
  22. break;
  23. }
  24. }
  25. string rres = "";
  26. //判断计算结果是否为24
  27. if (InfixExpressionEvaluation(res) == 24)
  28. //将中缀表达式转成string格式
  29. for (string s : res)
  30. rres += s;
  31. return rres;
  32. }

3.3  产生每个排列的全部顺序

  1. vector<string> dfs(vector<int>& nums){
  2. //N决定了需要插入运算符的个数,进而决定了循环的层数
  3. vector<string> resv;
  4. if (N == 4) {
  5. for (int i = 0; i < op.size(); i++) {//三个循环枚举所有的操作符顺序可能
  6. for (int j = 0; j < op.size(); j++) {
  7. for (int k = 0; k < op.size(); k++) {
  8. operation = { op[i],op[j],op[k] };//枚举所有可能的运算符顺序
  9. string res = cal(nums, operation);//计算结果
  10. if (res != "")
  11. resv.push_back(res);
  12. }
  13. }
  14. }
  15. }
  16. else if (N == 5) {
  17. for (int i = 0; i < op.size(); i++) {//三个循环枚举所有的操作符顺序可能
  18. for (int j = 0; j < op.size(); j++) {
  19. for (int k = 0; k < op.size(); k++) {
  20. for (int n = 0; n < op.size(); n++) {
  21. operation = { op[i],op[j],op[k], op[n] };//枚举所有可能的运算符顺序
  22. string res = cal(nums, operation);//计算结果
  23. if (res != "")
  24. resv.push_back(res);
  25. }
  26. }
  27. }
  28. }
  29. }
  30. else {
  31. for (int i = 0; i < op.size(); i++) {//三个循环枚举所有的操作符顺序可能
  32. for (int j = 0; j < op.size(); j++) {
  33. for (int k = 0; k < op.size(); k++) {
  34. for (int n = 0; n < op.size(); n++) {
  35. for (int m = 0; m < op.size(); m++) {
  36. operation = { op[i],op[j],op[k], op[n], op[m] };//枚举所有可能的运算符顺序
  37. string res = cal(nums, operation);//计算结果
  38. if (res != "")
  39. resv.push_back(res);
  40. }
  41. }
  42. }
  43. }
  44. }
  45. }
  46. return resv;
  47. }

3.4 逆波兰表达式求值

  1. int InversePolishevaluation(vector<string>& exception) {//逆波兰表达式求值
  2. //这里的表达式只涉及四则运算,不涉及括号
  3. stack<int> stk;
  4. int n = exception.size();
  5. for (int i = 0; i < n; i++) {
  6. string& exp = exception[i];
  7. if (isNumber(exp)) {
  8. stk.push(atoi(exp.c_str()));
  9. }
  10. else {
  11. int num2 = stk.top();
  12. stk.pop();
  13. int num1 = stk.top();
  14. stk.pop();
  15. switch (exp[0]) {
  16. case '+': stk.push(num1 + num2); break;
  17. case '-': stk.push(num1 - num2); break;
  18. case '*': stk.push(num1 * num2); break;
  19. case '/': stk.push(num1 / num2); break;
  20. }
  21. }
  22. }
  23. return stk.top();
  24. }

3.5 中缀表达式转逆波兰表达式

  1. int InfixExpressionEvaluation(vector<string>& exception) {//将中缀表达式转成逆波兰表达式
  2. stack<string> stk;
  3. vector<string> vt;
  4. int n = exception.size();
  5. for (int i = 0; i < n; i++) {
  6. string& exp = exception[i];
  7. if (isNumber(exp))
  8. vt.push_back(exp);
  9. else {
  10. out:
  11. if (exp == "*" || exp == "/") {
  12. if (stk.empty() || stk.top() == "+" || stk.top() == "-")
  13. stk.push(exp);
  14. else {
  15. vt.push_back(stk.top());
  16. stk.pop();
  17. goto out;
  18. }
  19. }
  20. else {
  21. if (stk.empty())
  22. stk.push(exp);
  23. else {
  24. vt.push_back(stk.top());
  25. stk.pop();
  26. goto out;
  27. }
  28. }
  29. }
  30. }
  31. while (!stk.empty()) {
  32. vt.push_back(stk.top());
  33. stk.pop();
  34. }
  35. return InversePolishevaluation(vt);
  36. }

3.6  运行结果(vs2017)

4  总结 

整体思路就是在所有的操作数之间插入运算符,对操作数和运算符分别全排列再组合成中缀表达式得到所有情况。再依次将每个中缀表达式转成逆波兰表达式进行计算,若计算结果为24,则记录本次的中缀表达式,最后将所有的记录输出。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/64312
推荐阅读
相关标签
  

闽ICP备14008679号