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

91 篇文章 2 订阅
47 篇文章 0 订阅

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 long
using 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;
					else
						ans=(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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值