HDU 5396 Expression(区间DP,排列组合)

来源:互联网 发布:python提取网页数据 编辑:程序博客网 时间:2024/06/09 15:57

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

思路很特别。大神的详细题解:http://www.cnblogs.com/chenchengxun/p/4741439.html

对于加减法,分成左右两块的话,对于左边的某一种运算情况,都要和右边的所有情况匹配,所以【左边】*【右边的全排列】。右边同理。

特别拎出来 乘法为什么不用乘上全排列:

假设我们dp[i][k] 里面所有的值是 (x1+x2+x3...xn)
假设我们dp[k+1][j] 里面所有的值是 (y1+y2+y3...yn)
dp[i][k] * dp[k+1][j] = (x1+x2+...xn) * (y1+y2+y3...yn) = x1*y1+...+x1*yn+x2*y1+...+x2*yn+xn*y1+...+xn*yn 无论怎么变换左(右)边的运算顺序,相乘出来的结果都是是一样的

最后要对结果乘上一个组合数。因为可以先算一会左边再算一会右边。

#include<iostream>#include<algorithm>#include<string>#include<map>//int dx[4]={0,0,-1,1};int dy[4]={-1,1,0,0};#include<set>//int gcd(int a,int b){return b?gcd(b,a%b):a;}#include<vector>#include<cmath>#include<stack>#include<string.h>#include<stdlib.h>#include<cstdio>#define mod 1000000007#define ll long longusing namespace std;ll x[105];int y[105];ll dp[105][105];ll A[105],C[105][105];int main(){A[0]=1;    for(int i=1;i<=100;i++)        A[i]=(A[i-1]*i)%mod;    C[0][0]=1;    for(int i=1;i<=100;i++){        C[i][0]=1;        for(int j=1;j<=i;j++)            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=1;i<=n;++i){cin>>x[i];}getchar();char c;for(int i=1;i<=n-1;++i){cin>>c;if(c=='-')y[i]=0;else if(c=='+')y[i]=1;else if(c=='*')y[i]=2;}for(int i=1;i<=n;++i)dp[i][i]=x[i];for(int i=2;i<=n;++i){for(int l=1;l<=n-i+1;++l){int r=l+i-1;ll ans;for(int k=l;k<r;++k){if(y[k]==0)ans=(dp[l][k]*A[r-k-1]-dp[k+1][r]*A[k-l])%mod;else if(y[k]==1)ans=(dp[l][k]*A[r-k-1]+dp[k+1][r]*A[k-l])%mod;elseans=(dp[l][k]*dp[k+1][r])%mod;dp[l][r]=(dp[l][r]+ans*C[(k-l)+(r-k-1)][k-l])%mod;//cout<<l<<" "<<r<<" /"<<dp[l][r]<<endl;}}}cout<<(dp[1][n]+mod)%mod<<endl;}    return 0;}


1 0