HDU 5396 Expression (区间dp)

本文提供了一道来自HDU OJ的算法题解析,该题涉及区间DP算法,通过动态规划计算不同运算符组合下表达式的总和。文章详细介绍了状态转移方程,并给出了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

题意:给出n个数,每相邻的两个数之间有一个运算符号,“+”, “-” 或 “*”,你进行n - 1次操作,每次操作选出一个符号c和它左右的两个数a和b,计算出 M = a c b,将得到的数代替 a c b 得到新的式子,直到最后式子为一个数。求按不同顺序选择符号得到的数的和。

思路:

dp[ i ][ j ] 表示序列为 [ i, j ] 时得到的数的和, j - i 大的答案由 j - i 小的答案递推得到,区间dp。

将 [ i, j ] 分为两部分,k为连接两部分的符号的位置:

1)加法 :dp[i][k] * A[j - k - 1] + dp[k + 1][j] * A[k - i]  // 阶乘 A [j - k - 1]对应右边得到的数有多少个

2)减法 :dp[i][k] * A[j - k - 1] - dp[k + 1][j] * A[k - i]

3)乘法: dp[i][k] * dp[k + 1][j]

还需要注意,假设左边得到一个数的符号顺序为f1,f2,f3,右边得到一个数的符号顺序为g1,g2,(k 为 [ i, j ] 的最后一个操作符号),总的符号顺序可能是f1,f2,g1,f3,g2,也可以是g1,f1,f2,f3,g2,共C(5,3)种可能,所以还要乘C(j - i - 1,k - i)

计算C[][]的时候要用组合数递推公式,因为除法不能取模。

还要注意取模

#include <stdio.h>
#include <string.h>
using namespace std;
typedef long long ll;
int a[105];
ll A[105], dp[105][105] ,C[105][105];
const ll MOD = 1000000007;
char c[105];

int main()
{
    #ifdef LOCAL
    freopen("in.txt", "r", stdin);
    #endif

    int n;
    A[0] = 1;
    for(int i = 1; i <= 103; i++)
    {
        A[i] = A[i - 1] * i % MOD;
    }
    C[0][0] = 1;
    for(int i = 1; i <= 103; i++)
    {
        C[i][0] = C[i][i] = 1;
        for(int j = 1; j < i; j++)
        {
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
        }
    }
    while(scanf("%d", &n) != EOF)
    {
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            dp[i][i] = a[i];
        }
        scanf(" %s", &c[1]);
        for(int jj = 1; jj <= n; jj++)
        {
            for(int i = 1; i + jj <= n; i++)
            {
                int j = i + jj;
                for(int k = i; k < j; k++)
                {
                    int cur = 0;
                    if(c[k] == '+')
                    {
                        cur = dp[i][k] * A[j - k - 1] % MOD + dp[k + 1][j] * A[k - i] % MOD;
                    }
                    else if (c[k] == '-')
                    {
                        cur = dp[i][k] * A[j - k - 1] % MOD - dp[k + 1][j] * A[k - i] % MOD;
                    }
                    else
                    {
                        cur =  dp[i][k] * dp[k + 1][j] % MOD;
                    }
                    cur %= MOD;
                    dp[i][j] += cur * C[j - i - 1][k - i];
                    dp[i][j] %= MOD;
                }
            }
        }
        printf("%I64d\n", (dp[1][n]+ MOD) % MOD);
    }

    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值