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;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文:http://blog.csdn.net/lvshubao1314/article/details/47780675