zoukankan      html  css  js  c++  java
  • hdu5396 Expression 区间dp +排列组合

    #include<stdio.h>
    #include<string>
    #include<map>
    #include<vector>
    #include<cmath>
    #include<stdlib.h>
    #include<string.h>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    const int N=105;
    const int MOD=1e9+7;
    int n;
    int a[N];
    char ch[N];
    void rd(int&x){
        char ch;
        for(ch=getchar();ch<'0' || ch>'9';ch=getchar());
        x=0;
        for(;ch>='0' && ch<='9';ch=getchar()) x=x*10+ch-'0';
    }
    long long res;
    long long sum[N][N][N];  /*sum[i][j][k]表示区间i到j最后一个取第k个符号的答案和   ,sum[i][j][n+1]表示区间i到的答案总和*/
    long long A[N];
    long long calc(long long x,long long y,char c){
        if(c=='+') return (x+y)%MOD;
        else if(c=='-') return (x-y+MOD)%MOD;
        else return (x*y)%MOD;
    }

    long long C[N][N];
    void solve(){
        memset(sol,0,sizeof sol);
        memset(sum,0,sizeof sum);
        for(int i=1;i<=n;i++){
            sum[i][i][n+1]=a[i];
        }
        for(int i=1;i<n;i++){
            sum[i][i+1][n+1]=sum[i][i+1][i]=calc(a[i],a[i+1],ch[i]);
        }
        for(int j=3;j<=n;j++)
            for(int i=1;i+j-1<=n;i++){
                int l=i,r=i+j-1;
                for(int k=l;k<r;k++){
                    if(ch[k]=='+'){
                    /*
                    第k个符号左边   第l到k个数字。这个区间的方案个数 A[k-l]          sum[l][k][n+1] l到k的和
                    第k个符号右边   第k+1到r个数字  这个区间的方案个数 A[r-k-1]    sum[k+1][r][n+1]  k+1到r的和
                    设左边 A[k-l]个值   a1,a2, ........   ai指的都是左边这个区间由某一顺序得到的一个值,能够将它和一个操作序列等同
                    设右边 A[r-k-1] 个值   b1,b2, ........   
                    (a1+a2........) +  (b1,b2.............)         对于每一个a,被加A[r-k-1]次 ,对于每一个b。被加A[k-l]次
                    减法同理
                    乘法特殊一点
                    (a1+a2........) *  (b1,b2.............)  乘法分配率,直接将两部分的总和相乘就可以
                */
                sum[l][r][k]=(sum[l][k][n+1]*A[r-k-1]+sum[k+1][r][n+1]*A[k-l])%MOD;
                    }else if(ch[k]=='-'){
                        sum[l][r][k]=(sum[l][k][n+1]*A[r-k-1]-sum[k+1][r][n+1]*A[k-l])%MOD;
                        sum[l][r][k]=(sum[l][r][k]+MOD)%MOD;
                    }else{
                        sum[l][r][k]=(sum[l][k][n+1]*sum[k+1][r][n+1])%MOD;
                    }
                    /*
                    之前算的是(左边的选择序列)+(右边的选择序列)。可是左右两边也有先后。再乘个组合数,比赛时这个想了好久才想到
                    对于上面的每一个ai和bj。他们在  遇到区间的最后一个操作符号后,须要再乘以一个组合数,由于之前的ai和bj是这种
                    ai:(操作1,操作2,操作3.。。。。操作n)+  bj:(操作n+1,操作n+2,操作n+3.。

    。。

    。)。可是不一定是ai的操作序列全在前面的。至于为什是组合不是排列
                    由于每一个ai和bj自己内部已经是有序了的。感觉这样讲的挺清楚了吧。自己比赛的时候这里卡了挺就久的。可是细致想一下。还是能够明确的
                    */
                       sum[l][r][k]=(sum[l][r][k]*C[r-l-1][k-l])%MOD;
                       sum[l][r][n+1]=(sum[l][r][n+1]+sum[l][r][k])%MOD;
                       sum[l][r][n+1]=(sum[l][r][n+1]+MOD)%MOD;
                }
            }
            printf("%I64d ",sum[1][n][n+1]);
    }
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("aaa","r",stdin);
    #endif
        int T;
        int q;
        A[0]=1;/*A是全排列 C是组合 ,预处理这两个*/
        for(int i=1;i<N;i++) A[i]=(A[i-1]*i)%MOD;
        
        for(int i=0;i<N;i++) C[i][0]=1;
        for(int i=1;i<N;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;
        }
        while(~scanf("%d",&n)){
            for(int i=1;i<=n;i++) scanf("%d",&a[i]);
            scanf("%s",ch+1);
            solve();
        }
        return 0;
    }




  • 相关阅读:
    winform发布桌面程序后提示需开启“目录浏览”
    asp手动给combox赋值
    博客园宣传视频
    Flash相册-------3D旋转应用
    C#获取当前时间与同步时间
    数据库操作sql server2014
    Css样式
    表的删除
    四叶草默认启动设置方法
    常用Linux命令
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/7098404.html
Copyright © 2011-2022 走看看