Expression
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 736 Accepted Submission(s): 427
He wants to erase numbers one by one. In i -th round, there are n+1−i 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 n−1 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+4∗6−8∗3 " is 1+4∗6−8∗3→1+4∗(−2)∗3→1+(−8)∗3→(−7)∗3→−21 .
For each test case, the first line contains one number n(2≤n≤100) .
The second line contains n integers a1,a2,⋯,an(0≤ai≤109) .
The third line contains a string with length n−1 consisting "+","-" and "*", which represents the operator sequence.
3 3 2 1 -+ 5 1 4 6 8 3 +*-*
2 999999689HintTwo numbers are considered different when they are in different positions.
题意:给你n个数和n-1个操作符(2<=n<=100),问你不断地每次给操作符的左右相邻的两个数加括号,直到最后
只剩一个数为止,问你不同的方案的所有最终剩下一个数的和是多少。在一次取操作符时只要操作符的位置不相同就
算一种方案。
思路:这题主要在于状态转移方程。设dp[i][j]为第i个数到第j个数的所有方案的和,那么假设我们已经枚举到区间
i,j(i < j),对于加法和减法这两种情况是类似的,我们考虑第k个操作符(i<=k<j,假设k这个操作符是最后一次做
的,两边的操作符都已经完成了),有
若‘+’:
t= (dp[i][k] * A[j - (k + 1)] + dp[k + 1][j] * A[k - i] ) % mod
若‘-’:
t= (dp[i][k] * A[j - (k + 1)] - dp[k + 1][j] * A[k - i] ) % mod
若‘*’:
t= (dp[i][k] * dp[k + 1][j]) % mod
其中A数组储存阶乘值,C数组储存组合值。
为什么加法减法要乘以阶乘?对于左边的i到k来说,假设右边有n个符号,那么右边肯定有n!种方案,对于每个方案
都要加上左边的总和,所以要乘以一个阶乘。同理右边。。。。
为何乘法不需要阶乘?因为假设两边所有情况的最后一个数为(x1,x2,x3,……,xn)(y1,y2,y3……,yn),两边的所有情
况的和应该是
x1*y1+x1*y2+x1*y3……+x1*yn+x2*y1+x2*y2+x2*y3……+x2*yn+x3*y1+x3*y2+x3*y3……+x3*yn+………+xn*y1+xn*y2+xn*y3……+xn*yn,
然后化简之后得到的其实就是
(x1+x2+x3+……+xn)*(y1+y2+y3+……+yn)
最后的时候我们要乘上一个组合数。
dp[i][j] = (dp[i][j] + t*C[j-i-1][k-i])%mod;
为什么要乘组合数?因为k左边的是一个整体,k右边是一个整体,现在我们dp[i][j]这个整体区间是由左边和右边一起
得到,这意味着我们首先肯定要先计算左边所有的运算符,或者先计算右边所有的运算符,所以从所有可选符号里面
先选左边个,剩下的自然就是右边个,这样总个数才是正确的。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int x[110];
char s[110];
ll dp[210][210];
ll A[110],c[110][110];
ll fdp(int a,int b)
{
if(dp[a][b]!=0)return dp[a][b];
if(a==b)
return dp[a][b]=x[a];
for(int i=a; i<b; i++)
{
ll t=0;
if(s[i]=='+')
t=(fdp(a,i)*A[b-i-1]+fdp(i+1,b)*A[i-a])%mod;
else if(s[i]=='-')
t=(fdp(a,i)*A[b-i-1]-fdp(i+1,b)*A[i-a])%mod;
else
t=(fdp(a,i)*fdp(i+1,b))%mod;
dp[a][b]=(dp[a][b]+t*c[b-a-1][i-a])%mod;
}
return dp[a][b];
}
int main()
{
A[0]=1;
for(int i=1; i<=100; i++)
A[i]=(A[i-1]*i)%mod;
for(int i=0; i<=100; i++)
for(int j=0; j<=i; j++)
if(j==0||j==i)
c[i][j]=1;
else c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
int n;
while(~scanf("%d",&n))
{
memset(dp,0,sizeof(dp));
for(int i=0; i<n; i++)
scanf("%d",&x[i]);
for(int i=0; i<n-1; i++)
scanf(" %c",&s[i]);
cout<<(fdp(0,n-1)+mod)%mod<<endl;
}
return 0;
}