Dreisam Equations

Time Limit: 10 Seconds      Memory Limit: 32768 KB      Special Judge

During excavations in the Dreisamwuste, a desert on some far away and probably uncivilized planet, sheets of paper containing mysterious symbols had been found. After a long investigation, the project scientists have concluded that the symbols might be parts of equations. If this were true, it would be proof that the Dreisamwuste was civilized a long long time ago.

The problem, however, is that the only symbols found on the sheets are digits, parantheses and equality signs. There is strong evidence that the people living in the Dreisamwuste knew only of three arithmetic operations: addition, subtraction, and multiplication. It is also known that the people of the Dreisamwuste did not have prioritization rules for arithmetic operations - they evaluate all terms strictly left to right. For example, for them the term 3 + 3 * 5 would be equal to 30, and not 18.

But currently, the sheets do not contain any arithmetic operators. So if the hypothesis is true, and the numbers on the sheets form equations, then the operators must have faded away over time.

You are the computer expert who is supposed to find out whether the hypothesis is sensible or not. For some given equations (without arithmetic operators) you must find out if there is a possibility to place +, -, and * in the expression, so that it yields a valid equation. For example, on one sheet, the string ``18=7 (5 3) 2" has been discovered. Here, one possible solution is ``18=7+(5-3)*2". But if there was a sheet containing ``5=3 3", then this would mean that the Dreisamwuste people did not mean an equation when writing this.


Input 

Each equation to deal with occupies one line in the input. Each line begins with a positive integer (less than 230) followed by an equality sign =. (For your convenience, the Dreisamwuste inhabitants used equations with trivial left sides only.) This is followed by up to 12 positive integers forming the right side of the equation. (The product of these numbers will be less than 230.) There might be some parentheses around groups of one or more numbers. There will be no line containing more than 80 characters. There is no other limit for the amount of the parentheses in the equation. There will always be at least one space or parenthesis between two numbers, otherwise the occurrence of white space is unrestricted. 

The line containing only the number 0 terminates the input, it should not be processed.


Output 

For each equation, output the line ``Equation #n:", where n is the number of the equation. Then, output one line containing a solution to the problem, i. e. the equation with the missing +, -, and * signs inserted. Do not print any white space in the equation. 

If there is no way to insert operators to make the equation valid, then output the line ``Impossible".

Output one blank line after each test case.


Sample Input

18 = 7 (5 3) 2
30 = 3 3 5
18 = 3 3 5
5 = 3 3
0


Sample Output

Equation #1:
18=7+(5-3)*2

Equation #2:
30=3+3*5

Equation #3:
Impossible

Equation #4:
Impossible

 

       题目中每个等式的右边,是一些数字和括号,要求放置适当的运算符+,-和*,使运算结果等于左边。对于每个可以放置运算符的位置,都可以放置+,-和*,即解空间是一颗完全三叉树,所以时间复杂度是O(3^n)。

       本题采用回溯算法。

(1)       等式数据的读取

    使用数组表示等式: char a[100];

    该数组的位置指针为: int apos;

    因为不知道等式有多少项,所以每次读取一行:gets(a);

    如果字符串a中没有“=”,则表示输入结束。 而结束行的数字“0”,担心有多余的空格符。

(2)       构造伪表达式

    从数组a中分离出数字、括号,并确定加入运算符的位置,构成伪表达式,存放到数组: int b[100];

    该数组的位置指针为: int bpos;

    对样例数据: 18=7 (5 3)2,数组b的值如下:、

下标

0

1

2

3

4

5

6

7

8

7

-6

-1

5

-6

3

-2

-6

2

    表中粗斜体是数字,-1表示左括号,-2表示右括号,-6表示该位置应该有一个运算符。为了便于判断,使用数组表示运算符在数组b中的位置: int   op[30];

    该数组的位置指针为: int opos;

    如上例所示,数组op的值如下:

下标

0

1

2

1

4

7

    表示数组b中,位置为1、4和7的地方,是运算符。

(3)       构造伪表达式

    采用回溯算法构造表达式,函数是:      void backtrack(int dep);

    其中形参dep表示第几个运算符。

            如果dep=opos,表示所有的运算符构造完毕

    此时,表达式的结果等于左边的数iLeft时,即找到了答案。

           如果dep<opos,对dep个运算符,可以放置+,-和*,并进入相应的子树。

(4)       括号的处理

    采用递归函数: int bracket();

    这是一个间接递归函数。也就是说,如果一队括号中还有括号的话,则继续调用backet()函数。也可以使用堆栈的方法实现。

 

代码如下:

ZOJ 1145 Dreisam Equations(回溯)_sedZOJ 1145 Dreisam Equations(回溯)_数组_02
  1 # include<stdio.h>
  2 # include<string.h>
  3 # include<ctype.h>
  4 
  5 # define LEFT -1        //左括号
  6 # define RIGHT -2       //右括号
  7 # define MUL -3         //*号
  8 # define ADD -4         //+号
  9 # define SUB -5         //-号
 10 # define OP -6          //有运算符
 11 # define NONE -10
 12 
 13 char a[100];            //原始的等式数据
 14 int b[100];             //伪表达式
 15 int best[100];          //答案
 16 int op[30];             //运算符在数组b中位置
 17 int bn;             //数组b的项数
 18 int iLeft;          //等式左边的数
 19 int apos,bpos,opos;
 20 int possible;           //是否有解 
 21 
 22 //位置指针跳过空格
 23 void space()
 24 {
 25     while(a[apos] && (a[apos] == ' '))
 26         apos++;
 27 }
 28 
 29 int compute();
 30 //计算括号里面的值
 31 int bracket()
 32 {
 33     int sum;
 34     if(b[bpos] == LEFT)
 35     {
 36         bpos++;                 //跳过左括号
 37         sum = compute();        //计算括号里面的值(注意间接递归)
 38         bpos++;                 //跳过右括号
 39     }
 40     else
 41         sum = b[bpos++];        //没有括号
 42     return sum;
 43 }
 44 
 45 //计算表达式
 46 int compute()
 47 {
 48     int sum = bracket();    //右边第一个数
 49     //对每一个运算符进行计算
 50     while(b[bpos] == MUL || b[bpos]==ADD || b[bpos]==SUB)
 51     {
 52         int operation = b[bpos++];      //取出运算符
 53         int ret = bracket();            //取出下一个数
 54         //根据运算符进行计算
 55         switch(operation)
 56         {
 57         case MUL:
 58             sum *=ret;
 59             break;
 60         case ADD:
 61             sum += ret;
 62             break;
 63         case SUB:
 64             sum -= ret;
 65             break;
 66         }
 67     }
 68     return sum;
 69 }
 70 
 71 //回溯算法,构造表达式
 72 void backtrack(int dep)
 73 {
 74     if(possible) return;    //得到答案
 75     int i;
 76     //所有运算符的构造完毕
 77     if(dep==opos)
 78     {
 79         bpos = 0;
 80         int iRight = compute();     //右边表达式的值
 81         if(iRight == iLeft)         //得到答案
 82         {
 83             possible = 1;
 84             for(i=0; i<bn; i++)
 85                 best[i] = b[i];
 86         }
 87         return;
 88     }
 89     //当前节点的3个孩子结点,分别使用+,-和*运算符进行构造
 90     b[op[dep]] = MUL;
 91     backtrack(dep+1);
 92     b[op[dep]] = ADD;
 93     backtrack(dep+1);
 94     b[op[dep]] = SUB;
 95     backtrack(dep+1);
 96 }
 97 
 98 //输出结果
 99 void print(int *q)
100 {
101     printf("%d=",iLeft);
102     int i;
103     for(i=0; i<bn; i++)
104         switch(q[i])
105         {
106         case ADD:
107             printf("+");
108             break;
109         case MUL:
110             printf("*");
111             break;
112         case SUB:
113             printf("-");
114             break;
115         case LEFT:
116             printf("(");
117             break;
118         case RIGHT:
119             printf(")");
120             break;
121         case OP:
122             printf("?");
123             break;
124         default:
125             printf("%d",q[i]);
126             break;
127         }
128 }
129 int main()
130 {
131     int iCase = 0;
132     int i;
133     while(gets(a) && strchr(a,'='))
134     {
135         possible = 0;
136         for(i=0; i<90; i++)
137             b[i] = NONE;        //初值
138         apos = 0;
139         sscanf(a,"%d",&iLeft);  //左边的数
140         //将位置指针移到数字的后面
141         while(a[apos] && isdigit(a[apos])) apos++;
142         space();    
143         apos++;         //跳过"="
144         bn=0;
145         opos = 0;
146         while(space(),a[apos])
147         {
148             if(a[apos] == '(')      //左括号
149             {
150                 b[bn++] = LEFT;
151                 apos++;
152                 continue;
153             }
154             if(a[apos]==')')        //右括号
155             {
156                 b[bn++] = RIGHT;
157                 apos++;
158             }
159             else                //读取数字
160             {
161                 sscanf(a+apos,"%d",&b[bn++]);
162                 while(a[apos] && isdigit(a[apos])) apos++;
163             }
164             space();
165             if(a[apos] && a[apos] != ')')   //如果不是结尾和‘)’,则有一个运算符
166             {
167                 op[opos++] = bn;
168                 b[bn++] = OP;
169             }
170         }
171         //回溯算法,构造表达式
172         backtrack(0);
173         printf("Equation #%d:\n",++iCase);
174         //表达式中只有一项
175         if(bn==1 &&iLeft == b[0])
176             printf("%d=%d\n",iLeft,iLeft);
177         //无解
178         else if(bn==0 || !possible)
179             printf("Impossible\n");
180         else
181         {
182             print(best);
183             printf("\n");
184         }
185         printf("\n");
186     }
187     return 0;
188 }
View Code

 

把每一件简单的事情做好,就是不简单;把每一件平凡的事情做好,就是不平凡!相信自己,创造奇迹~~