最近在一本算法设计书上看到一个用枚举法填入运算符(或者是括号),使之运算结果满足一定条件。例如在5 5 5 5 5 这5个数之间插入+ - * / 或者是括号运算符,使之最后结果等于5。在这个过程中有几个需要注意的问题:
1、运算符优先级问题:括号>乘除>加减;
2、数据存储问题:运算数据,运算符,括号。并且这三种数据存在所能在的位置数量存在关系:n(括号)=n(运算数据)+1,n(运算数据)=n(运算符)+1。
3、括号加入问题:加入括号的目的是改变运算顺序,但有时加入括号并不改变运算顺序。例如 5+5+5+5+5 与 (5+5+5)+5+5 完全一样,没有任何区别。
针对以上情况,我的解决方案是:
1、按照优先级的不同分三步得到结果;
2、运算数据用 digit[]存储,运算符用 method[] 存储,括号用结构体表示:
struct parenthesis
{
int left;
int right;
};
其中left表示左括号位置,right表示右括号位置。
3、引入 bool check() 函数进行判断。如果返回true 则说明计算括号加入有误,进行下一步。通过分析发现:要使括号加入有意义,至少需要满足两种情况的一种:1、括号内部运算符不统一,即有第一级第二级运算的混合。2、括号内部全为加减,并且挨着括号外部的左右两个运算符至少有一个为第一级运算符,即乘除:
inline bool check(parenthesis par,int method[])
{
int i=0;
for( i=par.left;i<par.right-1;i++)
{
if(method[i]>=3)
continue;
else
break;
}
if(i==par.right-1)
return true;
for(i=par.left;i<par.right-1;i++)
{
if(3-method[i]>0)
continue;
}
if(i==par.right-1)
{
if(par.left>=1&&par.right<=digitnum-1)
{
if(method[par.left-1]<3&&method[par.right-1]<3)
return true;
else
return false;
}
if((par.left==0&&method[par.right-1]<3&&par.right<digitnum)||(par.right==digitnum&&method[par.left-1]<3))
return true;
else
return false;
}
}
此外,转为计算乘除引入函数:
double begincal(int* p_slow,int* p_fast,int b,double digit[],int method[])
{
int begin=b,end=b+(p_fast-p_slow);
double results;
if(*p_slow<3)
results=digit[begin+1];
else
results=digit[begin];
for(int i=method[begin]>=3?begin:(begin+1);i<=end;i++)
{
if(method[i]==3)
results*=digit[i+1];
if(method[i]==4)
results/=digit[i+1];
}
return results;
}
整体计算引入函数:
double calculate(double digit[],int method[], int n)//calculate the whole equation;
{
int* p_slow=method;
int* p_fast=method;
double results=0;
vector<double> vec;
vector<int> met;
for(int m=0;m<n;m++)
{
if(method[m]<3)
met.push_back(method[m]);
}
if(met.size()==0)
{
results=digit[0];
for(int i=0;i<n;i++)
{
if(method[i]==3)
results*=digit[i+1];
if(method[i]==4)
results/=digit[i+1];
}
return results;
}
int i=0;
while (i<n)
{
if(method[i]<3&&i==0)//决定第一个数是否堆入vec;
vec.push_back(digit[i]);
if((method[i]>=3&&(i+1)<n/*&&method[i+1]>=3*/)||(method[i]<3&&(i+1)<n&&method[i+1]>=3))
{
int b=i,j=i;
p_slow=&method[i];
p_fast=&method[i];
while((++j)<n&&method[j]>=3)
p_fast++;
results=begincal(p_slow,p_fast,b,digit,method);
vec.push_back(results);
if(j<n)
i=j;
else
break;
}
if(method[i]<3&&(i+1)<n&&method[i+1]<3)//决定中间数是否堆入;
{
vec.push_back(digit[i+1]);
i++;
}
if(method[i]<3&&i==(n-1))//决定末尾数是否堆入;
{
vec.push_back(digit[i+1]);
i++;
}
}
results=vec[0];
for(int k=0;k<met.size();k++)
{
if(met[k]==1)
results+=vec[k+1];
if(met[k]==2)
results-=vec[k+1];
}
return results;
}
为了方便显示,引入一个显示函数和一个转换函数:
char num2char(int i)//transfer the num to char;
{
if(i==1)
return '+';
if(i==2)
return '-';
if(i==3)
return '*';
if(i==4)
return '/';
}
void showithpar(parenthesis& par,double digit[],int method[])
{
for(int i=0;i<digitnum+1;i++)
{
if(i==par.left)
cout<<'(';
if(i<digitnum)
cout<<digit[i];
if(i==par.right-1)
cout<<')';
if(i<digitnum-1)
cout<<num2char(method[i]);
}
cout<<" ="<<expect<<endl;
}
主函数为:
int _tmain(int argc, _TCHAR* argv[])
{
int n_temp=0;
double digit[digitnum]={5,5,5,5,5};
int method[digitnum-1]={0},count=0;
double expect_temp=0;
for(int i1=1;i1<=4;i1++)
for(int i2=1;i2<=4;i2++)
for(int i3=1;i3<=4;i3++)
for(int i4=1;i4<=4;i4++)
{
method[0]=i1; method[1]=i2; method[2]=i3; method[3]=i4;
expect_temp=calculate(digit,method,digitnum-1);
if(expect==expect_temp)
{
cout<<"find the "<<++count<<" condition that satisfy the requirements:\n";
cout<<digit[0]<<num2char(i1)<<digit[1]<<num2char(i2)<<digit[2]<<num2char(i3)
<<digit[3]<<num2char(i4)<<digit[4]<<"="<<expect<<endl;
}
}
count=0;
int count_temp=0;
cout<<endl<<"if have the parenthesis:\n";
parenthesis par={0,0};
for(par.left=0;par.left<digitnum;par.left++)
for(par.right=par.left+2;par.right<=digitnum;par.right++)
{
if(par.left==0&&par.right==digitnum)
continue;
for(int i1=1;i1<=4;i1++)
for(int i2=1;i2<=4;i2++)
for(int i3=1;i3<=4;i3++)
for(int i4=1;i4<=4;i4++)
{
method[0]=i1; method[1]=i2; method[2]=i3; method[3]=i4;
if(check(par,method))
continue;
double* p_dig=new double[par.right-par.left];
int* p_met=new int[par.right-par.left-1];
for(int i=0;i<par.right-par.left;i++)
*(p_dig+i)=digit[i+par.left];
for(int i=0;i<par.right-1-par.left;i++)
*(p_met+i)=method[par.left+i];
expect_temp=calculate(p_dig,p_met,par.right-par.left-1);
if(par.left>=1&&method[par.left-1]==4)
{
if(expect_temp==0)
continue;
}
double* p_dig1=new double[digitnum-(par.right-par.left)+1];
int* p_met1=new int[digitnum-(par.right-par.left)];
for(int i=0;i<par.left;i++)
*(p_dig1+i)=digit[i];
*(p_dig1+par.left)=expect_temp;
for(int i=par.right;i<digitnum;i++)
*(p_dig1+par.left+i-par.right+1)=digit[i];
for(int i=0;i<par.left;i++)
*(p_met1+i)=method[i];
for(int i=par.right-1;i<digitnum-1;i++)
*(p_met1+par.left-1+(i-par.right+2))=method[i];
expect_temp=calculate(p_dig1,p_met1,digitnum-(par.right-par.left));
if(expect_temp==expect)
{
cout<<"find the "<<++count<<" condition that satisfy the requirements:\n";
showithpar(par,digit,method);
}
delete[] p_dig;
delete[] p_met;
delete[] p_dig1;
delete[] p_met1;
}
}
char ch;
cout<<"please input a char:\n";
cin.get(ch);
return 0;
}