C + + Hoj 24 points

Source: Internet
Author: User

"Problem description"

It's a classic game to count 24 points on 4 cards. General requirements are only allowed with subtraction and parentheses for arithmetic.
For example: 1,2,3,4 can calculate 24 with the expression (1+2+3)-24.

It is required to calculate how many implementations are implemented and how to output all implementations.

"Idea One"

The basic principle is to be exhaustive 4 integers all possible expressions, and then evaluate the expression.
Definition of expressions: expression = (expression|number) operator (Expression|number)
Because the 4 operators that can be used +-*/are all 2-tuple operators, only the 2-dollar operator is considered in this article. The 2-tuple operator receives two parameters, outputs the results of the calculation, and the output results participate in subsequent calculations.
As described above, the algorithm for constructing all possible expressions is as follows:
(1) Put 4 integers in the array
(2) The arrangement of two numbers in the array, a total of P (4,2) arrangement. For each arrangement,
(2.1) to +-*/each operator,
(2.1.1) Calculates the result based on the two numbers and operators in this arrangement
(2.1.2) Array: Remove the two numbers from the array and put the results of the 2.1.1 calculation into the array
(2.1.3) for the new array, repeat steps 2
(2.1.4) to restore an array: Add the two digits of this arrangement to the array and remove the results of the 2.1.1 calculation from the array
This can be seen as a recursive process. Step 2 is the recursive function. When there is only one number left in the array, this is the final result of the expression, at which point the recursion ends.
In the program, it is important to pay attention to recursive field protection and recovery, that is, the recursive call before and after the field status should be consistent. In the above algorithm, the recursive field is the exponential group, 2.1.2 changes the array for the next level of recursive invocation, 2.1.3 restores the array to ensure that the current recursive call obtains the next correct arrangement.
The function of parentheses () only changes the precedence of operators, that is, the order in which operators are evaluated. Therefore, in the above algorithm, there is no need to consider parentheses. Brackets are only required to be considered when outputting.

#include <cstdio> #include <iostream> #include <cmath> using namespace std;    +-*/1234 unsigned long int xx[1001];    int x=1;    int PD;        int F1 (int a, int b, int c, int d) {int sum;        Pd=0;        Char S1,s2,s3;  for (int j = 1; J <= 4; j + +) {for (int k = 1, K <= 4; k++) {for (int l = 1; L <= 4;                    l++) {sum = 0;                    sum + = A;                    Switch (j) {case 1:sum + = b; break; Case 2:sum-= b;                    Break Case 3:sum *= B;                    Break                                Case 4: {if (sum%b) {                            sum = 9999;                            } else {sum/= b;        }                }break;                    Default:break;                    } switch (k) {case 1:sum + = c; break; Case 2:sum-= c;                    Break Case 3:sum *= C;                    Break                                Case 4: {if (sum%c) {                            sum = 9999;                            } else {sum/= C;                    }}break;                    Default:break;                    } switch (l) {case 1:sum + = D; break; Case 2:sum-= D;                    Break Case 3:sum *= D;                    Break                Case 4: {if (sum%d)            {sum = 9999;                            } else {sum/= D;                    }}break;                    Default:break;                    } switch (j) {case 1:s1= ' + ';                    Case 2:s1= '-';                    Case 3:s1= ' * ';                    Case 4:s1= '/';                    } switch (k) {case 1:s2= ' + ';                    Case 2:s2= '-';                    Case 3:s2= ' * ';                    Case 4:s2= '/';                    } switch (l) {case 1:s3= ' + ';                    Case 2:s3= '-';                    Case 3:s3= ' * '; Case 4:s3= '/';;                            } if (sum = =) {if (x==0) {                            Xx[x]=a*1000000+b*100000+c*10000+d*1000+j*100+k*10+l;                            x + +;                        printf ("((%d%c%d)%c%d)%c%d==24\n", a,s1,b,s2,c,s3,d);                            } else {for (int i=1;i<=x;i++)                                {if ((a*1000000+b*100000+c*10000+d*1000+j*100+k*10+l) ==xx[i])                                    {pd=1;                                Break                                }} if (pd==0) {                                Xx[x]=a*1000000+b*100000+c*10000+d*1000+j*100+k*10+l;                                x + +; printf ("((%d%c%d)%c%d)%c%d==24\n ", a,s1,b,s2,c,s3,d);                    }} return 1;    }}}} return 0;        } int F2 (int a, int b, int c, int d) {int sum = 0;        sum = F1 (A, B, C, D) + F1 (A, B, D, c) + F1 (A, C, B, D) + F1 (A, C, D, b) + F1 (A, d, B, c) + F1 (A, D, C, b);        if (sum! = 0) {return 1;        } else {return 0;        }} int main () {int A, B, C, D;        int sum;        Cin >> a >> b >> c >> D;        sum = F2 (A, B, C, D) + F2 (b, A, C, D) + F2 (c, a, B, D) + F2 (d, a, b, c);        if (sum = = 0) {cout << ' N ' << Endl;    } return 0;   }

"Thinking Two"
Recursive mode
4 numbers, whichever is 2, uses an operation to get a number, and the number is combined with the other two numbers that are not involved in the operation, and the 3 numbers are then counted as 2 numbers, and the resulting result is put together with another number. The last two numbers are then calculated to see if the result is 24, and if so, we find an expression that satisfies the requirement. According to this idea, we can write recursive functions as follows:

Recursivecalc (array, array length)

Take 2 numbers in the array, and another number to make up the new array

Recursive call Recursivecalc (new array, array length-1)

Recursion to the end of the array length = 1 o'clock, this time only to see whether the elements in the array = 24, you can know whether this operation meets the requirements.

The principle of recursion is relatively simple, but there are some details that need to be noted:

1. The 2 numbers to be taken out of the operation should be two numbers in different positions, for example, we take 3 in the 1,2,3,4, and the other number cannot take 3.

2. When composing a new array, also note that it is a new array with numbers that do not participate in the operation.

3. If a division is encountered, be careful about the case of divisor = 0.

4. Because the division may produce fractions, a floating-point number is used in the operation and cannot be used as an integer. For example, 1,5,5,5 calculates a 24-point expression for (5-1/5) the case of processing fractions, and one way is to implement the fractional class on its own, and all the numbers are represented by fractional class objects.

5. Recursive printout is cumbersome, because the last number is 24, you do not know how this 24 is obtained by the operation of the procedure, so you need to save the intermediate operation steps so that the final result output.

In my implementation of the code, the output part of the recursion is also written to compare garbage, the better way is that each number is a member of an object, parameters are passed by the object, the object retains the expression.

BOOL Recursivecalc (double *parray, int ncount) {bool BRet = false;              if (1 = = ncount) {if (Fabs (parray[0]-24.0) < 0.0001) {g_total++;              Output (Parray[0]);          return true;      } else return false; } for (int i = 0, i < ncount; i++) {for (int j = 0; J < Ncount; J + +) {if              (j = = i)              {continue;              } Double f1 = Parray[i];              Double F2 = parray[j];              char *p = g_op;                   for (int nop = 1; nop <= 4; nop++, p++) {if ('/' = = *p && fabs (F2) < 0.0001)                  {continue;                  } Double f = operation (F1, F2, *p);                  Double *pnew = new Double[ncount-1];                  if (!pnew) {return bRet;} pnew[0] = f;                      for (int m = 1, n = 0; m < nCount-1; m++) {while (n = = I | | n = = j)                      {n++;                  } Pnew[m] = parray[n++];                  } bRet |= Recursivecalc (pnew, nCount-1);                  g_n2--;                  G_N1-= 2;                  Delete pnew;                  if (G_bonlygetone && bRet) {return true;  }}}} return bRet;   }
The frequency analysis of recursion method
The recursive algorithm for a given 4 numbers has the following arrangement, without regard to the commutative law of addition and multiplication:
4 Number of 2 numbers in the arrangement 12 kinds of operators = 48 kinds of
3 Number of 2 numbers in the arrangement 6 kinds of operators = 24 kinds of
2 Number of 2 numbers in the arrangement 2 kinds of operators = 8 kinds of
So a total of 48*24*8 species = 9216 cases
"Thinking three"
Postfix expression Methods
The suffix expression, also known as the inverse Polish expression, is a representation of the operator after the two numeric values to be calculated, and the benefit of the suffix expression is that it can be used without parentheses.
For example, (1-2)/(3-4) Such an expression, the suffix notation is: 12-34-/, when interpreting the suffix expression, we scan from left to right, encountered the operator to the left of the operator to take the nearest two values to operate, after the end of the operation of the operator and the corresponding two operands are replaced with the results of the operation, Then repeat the process until the final result is calculated.
For example, for 3, 6 5-8 + * This expression, we can calculate this:
Encountered number first regardless, encountered the first sign is a minus sign, looking forward two number, is 6 and 5, so is 6-5, the result = 1. Replace with 1 6 5-Get a new expression:
3 1 8 + * Next is the plus sign, 1+8 = 9, and the expression becomes: 3 9 *
The final result is 3*9 = 27.
and the original expression converted to our usual to see the infix notation is: 6-5+8, you can see the suffix expression to avoid the parentheses.
For a more detailed description of this expression, readers can search for an introduction.
The suffix expression as a stack operation is a way of understanding in many textbooks and articles, where adding values to the stack without any processing, adding operators to the stack is the top of the stack of 2 operands and operators to operate, the results are put back into the stack, so that each operator is added, The number of values in the stack is reduced by 1, as can be seen from the above figure, with each operator followed by a value in parentheses that is 1 smaller than the parent node. Each value is appended with a value that is 1 more than the parent node in parentheses.

For the calculation of 4 numbers, the values are represented by a, and the symbols are represented by +, which can be arranged as follows:
aaaa+++
aaa+a++
aaa++a+
aa+aa++
aa+a+a+
Next we only need to complete the 4 numbers, and all the combinations of the 3 symbols, we can calculate all possible cases without omission. the use of suffix expression requires the implementation of 4 numbers of the entire arrangement, about the whole arrangement is divided into ordered and all sorts of unordered, in both ways we can. Both of These approaches are implemented in my code. among them, the dictionary order (ordered) the whole arrangement of ideas reference:

http://blog.csdn.net/visame/article/details/2455396
In fact, in the STL algorithm function library has the dictionary order the entire arrangement function can use, in "algorithm" The file inside defines the Next_permutation function and the Prev_permutation function, respectively expresses the order to arrange the entire arrangement the next arrangement and the previous arrangement. The idea is the same as the idea in the given link. The method of non-ordered full arrangement is also more, there are many introductions on the net, I use a very interesting method that this article introduces (carry different number):

http://llfclz.itpub.net/post/1160/278490
The ordered full line code is as follows:

void setnextsortpermutation (int *pidx, int nlen)//adjusts to the next permutation of the ordered full arrangement  {      int ncuridx = nLen-1;      int nfindidx = NCURIDX;      while (nCurIdx-1 >= 0 && pidx[ncuridx-1] > Pidx[ncuridx])      {          ncuridx--;      }      if (nCurIdx-1 < 0)      {          return;      }      while (Pidx[nfindidx] < pidx[ncuridx-1])      {          nfindidx--;      }      int tmp = pidx[ncuridx-1];      PIDX[NCURIDX-1] = Pidx[nfindidx];      PIDX[NFINDIDX] = tmp;      for (int i = ncuridx, j = nLen-1; I < J; i++, j--)      {          tmp = pidx[i];          Pidx[i] = pidx[j];          PIDX[J] = tmp;      }  }  
The algorithm that adjusts to the next permutation of the operator is similar to carry processing in arithmetic, and we define the order of the operators to be +-*/. Then + + + the next arrangement is ++-, only need to change the last symbol (+1) into the next symbol, if the symbol is out of bounds, such as ++/, the last symbol is a division, plus 1, you need to go back to the plus sign, at the same time on the front of the symbol carry, in the process of rounding may produce cyclic carry, so The next permutation of ++/(+ +) is the next arrangement of +-+,+//(plus except) is-++ (minus + +). The corresponding code is as follows:
void Setnextopcomb (int *nop, int nlen)//adjusts to the next combination of operator combinations, and carry processing in similar operations  {      int ncuridx = nLen-1;      while (ncuridx >= 0 && 4 = = ++nop[ncuridx])      {          Nop[ncuridx] = 0;          ncuridx--;      }  }  
In the concrete simulation stack operation, I define a node structure to represent a numeric value or a symbol:
struct Node  {      int nType;      Union      {          Double D;          int op;      };  };  
Using list in the actual code, only the tail of the list is manipulated to simulate the stack. The code is as follows:
BOOL Calcstack (Node *node_all, int nlen) {std::list<node> lst;      Node node;      Char output[200] = {0};      Char tmp[30] = {0}; for (int i = 0; i < Nlen; i++) {if (0 = = Node_all[i].ntype) {lst.push_back (node_a              Ll[i]);              sprintf_s (TMP, +, "%2d", (int) node_all[i].d);          strcat_s (output, n, tmp);              } else if (1 = = Node_all[i].ntype) {sprintf_s (TMP, +, "%c", G_op[node_all[i].op]);              strcat_s (output, n, tmp);              Node F2 = Lst.back ();              Lst.pop_back ();              Node f1 = Lst.back ();              Lst.pop_back ();              if (ABS (F2.D) < 0.0001 && 3 = = Node_all[i].op) {return false;              } node.ntype = 0;              NODE.D = operation (f1.d, F2.D, G_op[node_all[i].op]);          Lst.push_back (node);      } else assert (0);      }Double F = lst.back (). D;          if (ABS (f-24.0) < 0.0001) {g_total++;          printf ("%s=%d\r\n", output, (int) f);      return true;  } return false;   }
The suffix expression method has 5 valid suffix expressions, assuming that the ABCD is 4 digits, + represents the symbol
The first type: abcd+++
The second type: abc+d++
The third type: ab+cd++
Fourth type: abc++d+
Fifth type: ab+c+d+
For each case, the probability of a full permutation of 4 digits is 4*4*4, so there are 24*64=1536 for each case =24,3
5 cases of total 1536*5 = 7680 permutations
Although the method of postfix expression is less than recursive in comparison with the data, because I use the list in the code to simulate the stack, so the actual code running efficiency, the recursive method is faster than the suffix expression method, consumes about only 1/10 of the suffix expression method. That is, a lower order of magnitude.

"Summary of the puzzle"
Compared to the direct play poker software in the form of computing 24-point game, some software for each topic only provide an answer, regardless of the actual solution how many kinds. And some versions are just the opposite, providing all the ways to arrange the answers. This means that for 1, 2, 3, 4 of these four numbers, although only 1x2x3x4 a solution, and the listed answers include 4x3x2x1, 2x1x4x3, and so on up to 24 kinds!
On the one hand, if there are multiple calculations, we should provide all the answers, not just one of them; on the other hand, we should avoid repeating different forms of providing the same answer. It is easier to give all the answers, and here we only discuss how to avoid repeating different forms of providing the same answer. For example, 1, 1, 3, 8 of these four numbers, 1x1x3x8 and 8x3x1x1 should be equivalent, only one of the answers is provided. So how do you determine that the two formulas are equivalent?

It is considered equivalent if one of the equations can be converted to another by several times of the addition and multiplication exchange law.

Therefore 1x1x3x8 and 8x3x1x1, 1x3x1x8, 1x8x3x1 are all equivalent, but not equivalent to 1-1+3x8.

Subtraction and division are treated in a similar way to addition and multiplication, so that 5+10x2-1 is equivalent to 5-1+2x10,5x10÷2-1 and 10÷2x5-1 respectively.
After defining the equivalent rules, you need to solve two problems that follow: If the program compares the two formulas according to the rule? Which formula should the software choose as the standard answer for the user? These two problems can be solved in the same way, that is, to establish a collation, the comparison of the formula by the collation of the addition and multiplication of the exchange law to transform, until the evolution of a "standard" formula, and finally see whether the same results. The answer that is provided to the user is the "standard" formula.
What is the "standard" formula? For users, standard formulas should be more natural than non-standard formulas. From small to large, first plus minus, first multiply after the visual habits of most people. In addition, multiplication, the value of a small number or formula is always in front. That is, the 6x4 is sorted after the 4x6,3x6+3x2 is sorted and 2x3+3x6 is obtained. For subtraction and division, subtract or divide by smaller values or formulas. When adding and subtracting together, the addition is performed first. When multiplication and division are together, the multiplication is performed first. In this way 5-1+10x2 is 5+2x10-1, and (5-1+4) X3 is sorted into 3x (4+5-1).
It is necessary to point out that the most natural expression of this writing is not necessarily the most easily thought of when a person is doing a problem. Small numbers are more convenient to calculate, and small intermediate results are more advantageous for subsequent steps. In the example 1, 2, 5, and 10 above, 5-1+2x10 may be more likely to be thought of than 5+2x10-1. In addition, for many people, the most difficult operation in the first step to make themselves sure to find the answer, the answer is most likely to be 2x10+ (5-1). Therefore, it is difficult to say which formula is the most suitable. Fortunately, the user is usually not so serious, you just have to give one of the formulas.
If you are a 24-point enthusiast, you must have heard of the classic 1, 5, 5, 5, 24-point exam. The answer to this question is (5-1÷5) X5, the largest of the special operations used in the score, only allow the use of fractions to solve. In fact, no less than 10 such topics, such as 2, 4, 10, 10, there are 3, 3, 7, 7 and so on. The game to support fractional operations, you can use fractional arithmetic and then take the approximate value of the way. But this approach is not elegant enough. It is best to design a fractional class, implement the arithmetic method, and whether you want operator overloading depends on your personal preference. The fractional class has two private member variables, one is a molecule and the other is the denominator. When representing integers, the denominator is 1. When you save intermediate results, the numerator and denominator do not have to be coprime. If you want to coprime, you can refer to the classical method of dividing.

Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.

C + + Hoj 24 points

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.