http://acm.hdu.edu.cn/showproblem.php?pid=5396
思路很特别。大神的详细题解:http://www.cnblogs.com/chenchengxun/p/4741439.html
对于加减法,分成左右两块的话,对于左边的某一种运算情况,都要和右边的所有情况匹配,所以【左边】*【右边的全排列】。右边同理。
特别拎出来 乘法为什么不用乘上全排列:
假设我们dp[i][k] 里面所有的值是 (x1+x2+x3...xn)
假设我们dp[k+1][j] 里面所有的值是 (y1+y2+y3...yn)
dp[i][k] * dp[k+1][j] = (x1+x2+...xn) * (y1+y2+y3...yn) = x1*y1+...+x1*yn+x2*y1+...+x2*yn+xn*y1+...+xn*yn 无论怎么变换左(右)边的运算顺序,相乘出来的结果都是是一样的
最后要对结果乘上一个组合数。因为可以先算一会左边再算一会右边。
#include<iostream>
#include<algorithm>
#include<string>
#include<map>//int dx[4]={0,0,-1,1};int dy[4]={-1,1,0,0};
#include<set>//int gcd(int a,int b){return b?gcd(b,a%b):a;}
#include<vector>
#include<cmath>
#include<stack>
#include<string.h>
#include<stdlib.h>
#include<cstdio>
#define mod 1000000007
#define ll long long
using namespace std;
ll x[105];
int y[105];
ll dp[105][105];
ll A[105],C[105][105];
int main(){
A[0]=1;
for(int i=1;i<=100;i++)
A[i]=(A[i-1]*i)%mod;
C[0][0]=1;
for(int i=1;i<=100;i++){
C[i][0]=1;
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
int n;
while(~scanf("%d",&n)){
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;++i){
cin>>x[i];
}
getchar();
char c;
for(int i=1;i<=n-1;++i){
cin>>c;
if(c=='-')
y[i]=0;
else if(c=='+')
y[i]=1;
else if(c=='*')
y[i]=2;
}
for(int i=1;i<=n;++i)
dp[i][i]=x[i];
for(int i=2;i<=n;++i){
for(int l=1;l<=n-i+1;++l){
int r=l+i-1;
ll ans;
for(int k=l;k<r;++k){
if(y[k]==0)
ans=(dp[l][k]*A[r-k-1]-dp[k+1][r]*A[k-l])%mod;
else if(y[k]==1)
ans=(dp[l][k]*A[r-k-1]+dp[k+1][r]*A[k-l])%mod;
else
ans=(dp[l][k]*dp[k+1][r])%mod;
dp[l][r]=(dp[l][r]+ans*C[(k-l)+(r-k-1)][k-l])%mod;
// cout<<l<<" "<<r<<" /"<<dp[l][r]<<endl;
}
}
}
cout<<(dp[1][n]+mod)%mod<<endl;
}
return 0;
}