一个非常基础的问题,建出表达式树后在树上DP即可。预处理时记录每个位置之前每种符号最近的出现位置可以做到线性,这里直接写了$O(n^2)$算法。
注意减法和除法不满足交换律所以要取最右边的一个作为当前子树的根,而乘方要取最左边的。以及注意区分减号和负号。
1 #include<cmath> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 using namespace std; 7 8 const int N=1010; 9 char s[N],op[N]; 10 double ans,val[N]; 11 int n,rt,top,stk[N],lnk[N],ls[N],rs[N]; 12 13 bool isop(char ch){ return ch=='+' || ch=='-' || ch=='*' || ch=='/' || ch=='^'; } 14 15 int build(int l,int r){ 16 if (lnk[l]==r) return build(l+1,r-1); 17 int rt=0,rt1=0,rt2=0,x=0; 18 rep(i,l,r){ 19 if (s[i]=='(') i=lnk[i]+1; 20 if (i>r) break; 21 if ((s[i]=='+' || s[i]=='-') && (i!=l && !isop(s[i-1]))) rt=i; 22 if (s[i]=='*' || s[i]=='/') rt1=i; 23 if (s[i]=='^' && !rt2) rt2=i; 24 } 25 if (rt) x=rt; else if (rt1) x=rt1; else x=rt2; 26 if (!x){ x=r; op[x]='!'; sscanf(s+l,"%lf",&val[x]); return x; } 27 op[x]=s[x]; ls[x]=build(l,x-1); rs[x]=build(x+1,r); return x; 28 } 29 30 double dfs(int x){ 31 if (op[x]=='!') return val[x]; 32 double a=dfs(ls[x]),b=dfs(rs[x]),res=0; 33 if (op[x]=='+') res=a+b; 34 if (op[x]=='-') res=a-b; 35 if (op[x]=='*') res=a*b; 36 if (op[x]=='/') res=a/b; 37 if (op[x]=='^') res=pow(a,b); 38 return res; 39 } 40 41 int main(){ 42 freopen("cal.in","r",stdin); 43 freopen("cal.out","w",stdout); 44 scanf("%s",s+1); n=strlen(s+1); 45 rep(i,1,n){ 46 if (s[i]=='(') stk[++top]=i; 47 if (s[i]==')') lnk[stk[top]]=i,lnk[i]=stk[top],top--; 48 } 49 rt=build(1,n); printf("%.3lf ",dfs(rt)); 50 return 0; 51 }