hdu 5396 Expression(区间dp,组合数,好题)

题目链接

Expression

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 835    Accepted Submission(s): 497


Problem Description
Teacher Mai has  n  numbers  a1,a2,,an and  n1  operators("+", "-" or "*") op1,op2,,opn1 , which are arranged in the form  a1 op1 a2 op2 a3  an .

He wants to erase numbers one by one. In  i -th round, there are  n+1i  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  n1  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+4683 " is  1+46831+4(2)31+(8)3(7)321 .
 

Input
There are multiple test cases.

For each test case, the first line contains one number  n(2n100) .

The second line contains  n  integers  a1,a2,,an(0ai109) .

The third line contains a string with length  n1  consisting "+","-" and "*", which represents the operator sequence.
 

Output
For each test case print the answer modulo  109+7 .
 

Sample Input
  
  
3 3 2 1 -+ 5 1 4 6 8 3 +*-*
 

Sample Output
  
  
2 999999689
Hint
Two numbers are considered different when they are in different positions.
 

Author
xudyh




题意

给出一个有n个数字,n-1个运算符,组成只有"+","-","*"的表达式,对于这个表达式,可以在不同的位置加括号得到不同的计算顺序,

只要有一个计算顺序不同这两个表达式就是不同的。求所有运算方式所得到的最终结果之和对1e9+7取模的值。

数据

(2 <= n <= 100), n个整数ai(0 <= ai <= 1e9)。

输入

3

3 2 1

-+

5

1 4 6 8 3

+*-*

输出

2

999999689


wannafly公众号上面题解:
思路

如果将这个过程随机化,即每次等概率地选取相邻两项合并,记dp[i][j]为随机合并第i个到第j个数字这一段的表达式之后结果的期望,根据期望的线性可加性,状态转移方程为,其中”?”表示第k个数与第k+1个数之间的运算符,那么dp[1][n]即为随机合并整个表达式之后结果的期望,乘上方案数(n-1)!即为所求的总和,由于是取模意义下的运算,转移方程中的除法要用逆元代替,复杂度O(n^3),

参考代码

http://paste.ubuntu.com/23844964/



这个其实就是区间DP了
dp[i][j] 代表的是区间  i 到 j 的和
枚举dp[i][j] 之间所有的子区间
假如是乘法:
t = dp[i][k] * dp[k+1][j];
这个其实可以直接算出来的:
假设我们dp[i][k] 里面所有的值是 (x1+x2+x3...xn) == dp[i][k] (此处x1,x2...表示的是d[i][k]的一种组合的结果)
假设我们dp[k+1][j] 里面所有的值是 (y1+y2+y3...yn) == dp[k+1][j]
dp[i][k] * dp[k+1][j] == (x1+x2+...xn) * (y1+y2+y3...yn) == x1*y1+x1y*y2......xn*yn 其实和所有不同结果相乘出来是一样的
 
假如是加法或者减法:
我们表示阶乘 i为A[i].
t = dp[i][k]*A[j-k-1] + dp[k+1][j]*A[k-i];
其实这里我们想一下。区间 dp[i][k] 需要加上多少次?
我们需要加的次数就是另一半区间的所有组合数,另一半区间有多少种组合方式我们就要加上多少个。
因为他们之间可以相互组成不同的种类。同理另一半也是。
 
最后的时候我们要乘上一个组合数。
假设组合数为C[i][j].
为什么要乘组合数:
因为 假如我们k 分割了两个运算式子   (1+(2*3)  ) + ( 1+(3*4) )
虽然说我们左右两边的式子运算顺序已经确定了,但是我们总的运算顺序还是不确定的, 比如我们算完(2*3) 直接去算(3*4)也是不同的结果
dp[i][j] = dp[i][j] + t*C[j-i-1][k-i]
这个其实就是从总的运算符(j-i-1)(减去了第k个的运算符)个中选取(k-i)个进行运算。
因为我们选取数达到 k-i的时候,另外我们还需要保持左右两边运算的相对顺序。
就比如说:左边有a 个运算符, 右边有b个运算符。
我们从 a+b个位置中选取 a位置个放a个的运算符。其余的只能放另一边的的运算符了。因为我们左右两边的相对顺序是不变的。
 

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const int inf=0x3fffffff;
const ll mod=1e9+7;
const int maxn=100+10;
ll d[maxn][maxn],c[maxn][maxn],A[maxn],a[maxn];
char s[maxn];
void init()
{
    A[0]=1;
    c[0][0]=1ll;      //
    rep(i,1,maxn) A[i]=A[i-1]*i%mod;
    c[1][0]=c[1][1]=1ll;
    rep(i,2,maxn)
    {
        c[i][0]=c[i][i]=1ll;
        rep(j,1,i) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
    }
}
int main()
{
    int n;
    init();
    while(~scanf("%d",&n))
    {
        memset(d,0,sizeof(d));
        rep(i,1,n+1) scanf("%lld",&a[i]),d[i][i]=a[i];
        scanf("%s",s+1);
        for(int i=n-1;i>=1;i--)
        {
            for(int j=i+1;j<=n;j++)
            {
                for(int k=i;k<j;k++)
                {
                    if(s[k]=='+')
                    d[i][j]=(d[i][j]+(d[i][k]*A[j-k-1]%mod+d[k+1][j]*A[k-i]%mod)%mod*c[j-i-1][k-i]%mod)%mod;
                    else if(s[k]=='-')
                    d[i][j]=(d[i][j]+(d[i][k]*A[j-k-1]-d[k+1][j]*A[k-i])%mod*c[j-i-1][k-i]%mod+mod)%mod;
                    else
                    d[i][j]=(d[i][j]+(d[i][k]*d[k+1][j]%mod)*c[j-i-1][k-i]%mod)%mod;
                }
            }
        }
        while(d[1][n] < 0)//可能小于0的。。不加会wa
            d[1][n] += mod;
        printf("%lld\n",d[1][n]%mod);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值