HDU 5396 Expression( 组合+dp ??)

Expression

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 380    Accepted Submission(s): 214


Problem Description
Teacher Mai has  n  numbers  a1,a2,,an and  n1  operators("+", "-" or "*") op1,op2,,opn1 , which are arranged in the form  a1 op1 a2 op2 a3  an .

He wants to erase numbers one by one. In  i -th round, there are  n+1i  numbers remained. He can erase two adjacent numbers and the operator between them, and then put a new number (derived from this one operation) in this position. After  n1  rounds, there is the only one number remained. The result of this sequence of operations is the last number remained.


He wants to know the sum of results of all different sequences of operations. Two sequences of operations are considered different if and only if in one round he chooses different numbers.

For example, a possible sequence of operations for " 1+4683 " is  1+46831+4(2)31+(8)3(7)321 .
 

Input
There are multiple test cases.

For each test case, the first line contains one number  n(2n100) .

The second line contains  n  integers  a1,a2,,an(0ai109) .

The third line contains a string with length  n1  consisting "+","-" and "*", which represents the operator sequence.
 

Output
For each test case print the answer modulo  109+7 .
 

Sample Input
  
  
3 3 2 1 -+ 5 1 4 6 8 3 +*-*
 

Sample Output
  
  
2 999999689
Hint
Two numbers are considered different when they are in different positions.
 

Author
xudyh
 

Source
 


题目大意:给出n个数和n-1个运算符,运算符是按顺序放在两个数之间的,也就是说构成了一个中缀表达式。由于运算的先后顺序不同,得到的结果也可能不同,问这些不同的运算顺序得到的结果加和对(10^9+7)取模的结果。


解题思路:区间DP

dp[i][j]表示区间 i 到 j 的和,枚举dp[i][j]之间所有的子区间。


如果是乘法:s = dp[i][k]*dp[k+1][j]

假设dp[i][k]的值可以表示为dp[i][k]=x1+x2+x3+……+xn

假设dp[k+1][j]的值可以表示为dp[k+1][j]=y1+y2+y3+……+ym

则dp[i][k]*dp[k+1][j]=(x1+x2+……+xn)*(y1+y2+……+ym)=x1*y1+x1*y2+……+xn*ym

其实和所有不同结果相乘出来是一样的。


如果是加法或者是减法:

以fac[i]的值表示i的阶乘,s = dp[i][k]*fac[j-k-1]+dp[k+1][j]*fac[k-i]

其实这里我们想一下。区间 dp[i][k] 需要加上多少次?我们需要加的次数就是另一半区间的所有组合数,另一半区

间有多少种组合方式我们就要加上多少个。因为他们之间可以相互组成不同的种类。同理另一半也是。



最后我们还要乘上一个组合数。

假设组合数为C[i][j].

为什么要乘组合数:

因为 假如我们k 分割了两个运算式子   【 1+(2*3)  】 + 【 1+(3*4) 】

虽然说我们左右两边的式子运算顺序已经确定了,但是我们总的运算顺序还是不确定的, 比如我们算完(2*3)直接

去算(3*4)也是不同的结果

dp[i][j] = dp[i][j] + s*C[j-i-1][k-i]

这个其实就是从总的运算符(j-i-1)(减去了第k个的运算符)个中选取(k-i)个进行运算。

因为我们选取数达到 k-i的时候,另外我们还需要保持左右两边运算的相对顺序。

就比如说:左边有a 个运算符, 右边有b个运算符。

我们从 a+b个位置中选取 a位置个放a个的运算符。其余的只能放另一边的的运算符了。因为我们左右两边的相对顺

序是不变的。


代码如下:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#include <limits.h>
#define debug "output for debug\n"
#define pi (acos(-1.0))
#define eps (1e-6)
#define inf (1<<28)
#define mod 1000000007
#define sqr(x) (x) * (x)
using namespace std;
typedef long long ll;
typedef unsigned long long ULL;
const int MAX = 105;
ll n,dp[MAX][MAX],fac[MAX],c[MAX][MAX];
char op[MAX];
void init()
{
    ll i,j;
    fac[0]=1;
    for(i=1;i<=100;i++)
        fac[i]=i*fac[i-1]%mod;
    c[0][0]=1;
    for(i=1;i<=100;i++)
    {
        c[i][0]=1;
        for(j=1;j<=i;j++)
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
    }
}
int main()
{
    ll i,j,k,l;
    init();
    while(~scanf("%I64d",&n))
    {
        memset(dp, 0, sizeof(dp));
        for(i=1;i<=n;i++)
            scanf("%I64d",&dp[i][i]);
        scanf("%s",op+1);
        for(l=2;l<=n;l++)
        {
            for(i=1;i+l-1<=n;i++)
            {
                j=i+l-1;
                dp[i][j]=0;
                for(k=i;k<j;k++)
                {
                    ll s;
                    if(op[k]=='*')
                        s=(dp[i][k]*dp[k+1][j])%mod;
                    if(op[k]=='+')
                        s=(dp[i][k]*fac[j-k-1]+dp[k+1][j]*fac[k-i])%mod;
                    if(op[k]=='-')
                        s=(dp[i][k]*fac[j-k-1]-dp[k+1][j]*fac[k-i])%mod;
                    dp[i][j]=(dp[i][j]+s*c[j-i-1][k-i])%mod;
                }
            }
        }
        printf("%I64d\n",(dp[1][n]+mod)%mod);
    }
    return 0;
}





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值