http://acm.hdu.edu.cn/showproblem.php?pid=5396
3 3 2 1 -+ 5 1 4 6 8 3 +*-*
2 999999689HintTwo numbers are considered different when they are in different positions.
/** hdu 5396 区间dp+组合 题目大意:给定一个只含有+-*的表达式,让你加括号决定运算顺序,问所有运算顺序可得的总和是多少? 解题思路:看一眼就知道区间dp最后差一个地方没有调出来,悲剧了,看了题解才知道左右区间还要乘上组合数== (转) 用dp[l][r]表示第l个数到第r个数组成的各种顺序的表达式和是多少,t[l][r]表示第l个数到第r 个数有多少种不同的组合。dp[l][r]的计算方法是枚举最后一个被计算的位置i,设n1=dp[l][i], n2=dp[i+1][r],t1=t[l][i],t2=t[i+1][r]。那么对于加号,对于每个i要加上n1*t2+n2*t1,对于右边不同 的组合,左边的数每次都要被加一次,同理左边不同的组合,右边的数每次也要被加一次。因此n1被加了t2次, n2被加了t1次。减法和加法一样。乘法是直接n1*n2。这还没完,注意就算是左边的顺序和右边的顺序的确定, 假设左边有f1个符号,右边有f2个符号,也有C[f1+f2][f1]种排法,相当于在f1+f2个位置中选f1个,剩下的给f2, f1和f2中排列的相对顺序不改变,所以还要乘上C[f1+f2][f1]。同理对于每个i,t[l][r]要加上t1*t2*C[f1+f2][f1]。 */ #include <string.h> #include <algorithm> #include <iostream> #include <stdio.h> using namespace std; typedef long long LL; const LL mod=1e9+7; int n; LL a[105],dp[105][105],num[105][105],c[105][105]; char s[105]; char getC() { c[0][0]=1; for(int i=1;i<=100;i++) { c[i][0]=c[i][i]=1; for(int j=1;j<i;j++) { c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; } } } int main() { getC(); while(~scanf("%d",&n)) { for(int i=1; i<=n; i++) scanf("%I64d",&a[i]); scanf("%s",s+1); memset(num,0,sizeof(num)); memset(dp,0,sizeof(dp)); for(int i=1; i<=n; i++) { num[i][i]=1; dp[i][i]=a[i]; } for(int i=n-1; i>=1; i--) { for(int j=i+1; j<=n; j++) { LL cnt=0; for(int k=i; k<j; k++) { if(s[k]=='*') { cnt=(dp[i][k]*dp[k+1][j])%mod; } else if(s[k]=='-') { cnt=((dp[i][k]*num[k+1][j])%mod-(dp[k+1][j]*num[i][k])%mod)%mod; } else { cnt=((dp[i][k]*num[k+1][j])%mod+(dp[k+1][j]*num[i][k])%mod)%mod; } dp[i][j]=(dp[i][j]+c[j-i-1][k-i]*cnt%mod)%mod; num[i][j]=(num[i][j]+num[i][k]*num[k+1][j]%mod*c[j-i-1][k-i]%mod)%mod; } //printf("%d %d:%I64d\n",i,j,dp[i][j]); } } printf("%I64d\n",(dp[1][n]+mod)%mod); } return 0; }