您的位置:首页 > 其它

【区间dp】hdu5396 Expression

2016-07-26 13:28 169 查看
http://acm.hdu.edu.cn/showproblem.php?pid=5396

现在有n个(≤100)数字和n-1个操作,操作只有”+ - *”三种,并且顺序是a1 op1 a2 op2 a3 ⋯ an。

然后小明每一次随便选两个相邻的数和他们中间的符号,然后计算答案并且把那两个数擦掉换成新的数字。

执行n-1次后会得到一个数字,现在请问对于所有可能的操作顺序最后得到的结果,求和是多少?

思路:

dp[i][j]为第i个数字到第j个数字这段区间所有可能结果之和

考虑[i, j]这段区间内最后计算的符号为第k个符号,那么就应该是[i, k]这段区间的所有结果加减乘[k+1, j]这段区间的所有结果。

假设[i,k]区间的结果分别是A1,A2,A3...[k, j+1]区间的结果分别是B1,B2,B3....

那么dp[i][k] = A1 + A2 + A3 .... 同理 dp[k+1][j] = B1 + B2 + B3

当sign[k]为乘号时,结果分别是A1*B1,A1*B2.....A2*B1,A2*B2.......,由乘法结合律得:结果之和为dp[i][k] * dp[k+1][j]

当sign[k]为加减号时,结果分别为A1±B1,A1±B2.....A2±B1,A2±B2.......,那么结果之和为dp[i][k] * num[k+1][j] + dp[k+1][j] * num[i][k]

其中num[i][j]表示区间[i, j]合并完成有多少种顺序,其实也就等于区间长度的阶乘。

对于每一种[i, j]区间的结果,我们需要吧[i, k]区间的操作插到[k+1, j]区间的操作时间序列中,方法共有H(j - k, k - i) = C(j - i - 1, k - i)种

(对于同一结果,先执行左边操作或先执行右边操作虽然不影响算式结果但都要计算到答案中去)

于是对于每一个k,对于[i, j]区间的贡献是 sigma(计算结果) * 算出每个结果的方案数

注意最终答案要+mod再%mod防止出现负数(我也不知道为什么orz

#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

const int mod = 1e9 + 7;

int n, si[105];
int f[105][105];
int mul[105], C[105][105];

int num(int i, int k, int j)
{
int res = ( (long long)mul[k - i] * mul[j - k - 1] ) % mod;
res = ( (long long)res * C[j - i - 1][k - i] ) % mod;
return res;
}

int dp(int L, int R)
{
if (L == R || f[L][R]) return f[L][R];
for (int k = L; k < R; ++ k) {
long long tmp;
if (si[k] == 3) tmp = ( (long long)dp(L, k) * dp(k + 1, R) ) % mod;
else {
tmp = (long long) dp(L, k) * mul[R - k - 1];
if (si[k] == 1) tmp += (long long) dp(k + 1, R) * mul[k - L];
else tmp -= (long long) dp(k + 1, R) * mul[k - L];
tmp %= mod;
}
tmp *= C[R - L - 1][k - L]; tmp %= mod;
f[L][R] = ( (long long) f[L][R] + tmp ) % mod;
}
//printf("f[%d][%d] = %d\n", L, R, f[L][R]);
return f[L][R];
}

int main()
{
mul[0] = mul[1] = 1;
for (int i = 2; i <= 100; ++ i) {
mul[i] = ( (long long)mul[i - 1] * i ) % mod;
}
for (int i = 0; i <= 100; ++ i) C[i][0] = 1;
for (int i = 1; i <= 100; ++ i) {
for (int j = 1; j <= i; ++ j) {
C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
if (C[i][j] > mod) C[i][j] %= mod;
// printf("C(%d, %d) = %d\n", i, j, C[i][j]);
}
}
while (~scanf("%d", &n)) {
memset(f, 0, sizeof(f));
for (int i = 1; i <= n; ++ i)
scanf("%d", &f[i][i]);
char c;
for (int i = 1; i < n; ++ i) {
scanf(" %c", &c);
if (c == '+') si[i] = 1;
else if (c == '-') si[i] = 2;
else si[i] = 3;
}
printf("%d\n", (dp(1, n) + mod) % mod);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: