HDU 5396 Expression (数学期望+区间DP)

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5396


题意:

给出一个有n个数字,运算符只有"+","-","*"的表达式,每次合并相邻两项,求所有合并方式所得到的最终结果之和对1e9+7取模的值。


分析:

如果将这个过程随机化,即每次等概率地选取相邻两项合并,

记dp[i][j]为随机合并第i个到第j个数字这一段的表达式之后结果的期望,

根据期望的线性可加性,状态转移方程为

dp[i][j]=(sigma_(k=i~j-1)(dp[i][k]?dp[k+1][j]))/(j-i),

其中"?"表示第k个数与第k+1个数之间的运算符,

那么dp[1][n]即为随机合并整个表达式之后结果的期望,

乘上方案数(n-1)!即为所求的总和,

由于是取模意义下的运算,转移方程中的除法要用逆元代替,

复杂度O(n^3)。


代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=105;
const ll Mod=1000000007LL;
ll a[MAXN],f[MAXN];
char s[MAXN];
ll dp[MAXN][MAXN];
void build()
{
    f[0]=1LL;
    for(int i=1;i<=100;i++)
        f[i]=i*f[i-1]%Mod;
}
ll fp(ll a,ll k)
{
    ll res=1LL;
    while(k>0)
    {
        if(k&1)res=res*a%Mod;
        a=a*a%Mod;
        k>>=1;
    }
    return res;
}
int main()
{
    build();
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
            scanf("%I64d",&a[i]);
        scanf("%s",s+1);
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++)
            dp[i][i]=a[i];
        for(int len=1;len<n;len++)
        {
            for(int i=1;i+len<=n;i++)
            {
                int j=i+len;
                for(int k=i;k<j;k++)
                {
                    if(s[k]=='+')
                        dp[i][j]=(dp[i][j]+dp[i][k]+dp[k+1][j])%Mod;
                    else if(s[k]=='-')
                        dp[i][j]=(dp[i][j]+dp[i][k]-dp[k+1][j])%Mod;
                    else
                        dp[i][j]=(dp[i][j]+dp[i][k]*dp[k+1][j]%Mod)%Mod;
                }
                dp[i][j]=(dp[i][j]%Mod+Mod)%Mod;
                dp[i][j]=dp[i][j]*fp(len,Mod-2)%Mod;
            }
        }
        printf("%I64d\n",dp[1][n]*f[n-1]%Mod);
    }
    return 0;
}


你可能感兴趣的:(HDU 5396 Expression (数学期望+区间DP))