給定 n(≤ 100)個數字與 n−1 個運算符(+、−、*),按任意順序結合(每次選相鄰兩數及其間運算符計算),求所有可能運算順序的最終結果之和,模 109+7。
dp[l][r] = 區間 [l, r] 所有合法運算順序的結果總和。
cnt[l][r] = 該區間的合法運算順序數(卡特蘭數相關)。
枚舉最後一步在 k(l ≤ k < r)處結合:
*:+ 或 −:其中 C 為組合數,表示左右子區間操作序列的交織方式數。
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9+7, MAXN = 105;
long long dp[MAXN][MAXN], cnt[MAXN][MAXN], C[MAXN][MAXN];
int a[MAXN]; char op[MAXN];
int main() {
// 預計算組合數
for (int i = 0; i < MAXN; ++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;
}
int n;
while (cin >> n) {
for (int i = 1; i <= n; ++i) cin >> a[i];
string s; cin >> s;
for (int i = 1; i < n; ++i) op[i] = s[i-1];
// 初始化長度 1
for (int i = 1; i <= n; ++i) {
dp[i][i] = a[i] % MOD;
cnt[i][i] = 1;
}
// 區間 DP
for (int len = 2; len <= n; ++len) {
for (int l = 1; l + len - 1 <= n; ++l) {
int r = l + len - 1;
dp[l][r] = 0; cnt[l][r] = 0;
for (int k = l; k < r; ++k) {
long long ways = C[r-l-1][k-l];
if (op[k] == '*') {
dp[l][r] = (dp[l][r] + dp[l][k] * dp[k+1][r] % MOD * ways) % MOD;
} else if (op[k] == '+') {
dp[l][r] = (dp[l][r] + (dp[l][k] * cnt[k+1][r] + dp[k+1][r] * cnt[l][k]) % MOD * ways) % MOD;
} else { // '-'
dp[l][r] = (dp[l][r] + (dp[l][k] * cnt[k+1][r] - dp[k+1][r] * cnt[l][k]) % MOD * ways) % MOD;
}
cnt[l][r] = (cnt[l][r] + cnt[l][k] * cnt[k+1][r] % MOD * ways) % MOD;
}
}
}
cout << (dp[1][n] % MOD + MOD) % MOD << '\n';
}
return 0;
}
O(n3) 時間,n ≤ 100 約 106 狀態轉移,極快。