• NKOJ P3051浇花


    时间限制 : 10000 MS   空间限制 : 65536 KB
    问题描述

    n 个非负整数排成一行,每个数值为Ai,数的位置不可改变。需要把所有的数都恰好等于h。可进行的操作是:对任意长度的区间[i,j]中的每个数都加1,i 和j 也任选,但要求每个数只能作为一次区间的起点,也只能作为一次区间的终点。 也即是说: 对任意的两个区间[l1, r1] 和[l2, r2], 要求: l1≠l2 并且r1 ≠ r2.
    请问有多少种不同的方式,使所有的数都等于h.输出答案模1000000007 (10^9+7)后的余数。
    两种方式被认为不同,只要两种方式所实施的操作的区间集合中,有一个区间不同即可.

    输入格式

    第1 行:2 个整数n, h (1 ≤ n, h ≤ 2000)
    接下来n 行,每行1 个整数,表示Ai (1≤Ai≤2000)
    30%的数据,n, h <= 30
    100%的数据,n, h <= 2000

    输出格式

    第1 行:1 个整数,表示答案

    样例输入

    Sample1:
    3 2
    1 1 1
    Sample2:
    5 1
    1 1 1 1 1

    样例输出

    Sample1:
    4
    Sample2:
    1

    浇花
    考点 区间动态规划
    类似于括号 dp 的讨论方式,讨论 i 的左边,选哪个数字作为区间的起点,更新 i 的值
    dp[i][k]表示从左往右讨论到第 i 个数字,i 的左边有 k 个数字还未被用过(被当做区间的左
    起点), 的方案数。
    分两种情况讨论:
    情况 1:i 被别人更新(因为 i 前面的 k 个数,任选一个为区间起点,都可更新到 i):
    若 a[i]+k==h 则 dp[i][k]=dp[i+1][k-1]*k+dp[i+1][k]
    说明,条件 a[i]+k==h,因为 i 左边有 k 个数字还没用过,那么以这 k 个数字作为区间左起点可以操作 k 次,每次都可以更新到 i,更新 k 次,恰好就能使 a[i]变成 h。
    现在对于 i 而言,有两种选择, 使用 i 或者不使用 i。
    若用 i 作为区间右端点,因为 i 只能当一次区间终点,所以只能从前 k 个中选一个来与
    它配对,故有 k 种方案,k 个数中 i 选了一个,对于 i+1 它左边就只有 k-1 个未使用的数了,数
    量总数为 k*dp[i+1][k-1] 。
    注意,这里 i 不能再作为区间的左端点了,这样的话会导致 i 被多更新一次,高度变成
    h+1
    若不用 i 作为区间端点,则方案数为 dp[i+1][k]
    情况 2:i 作为区间起点去更新别人
    若 a[i]+k+1=h 则 dp[i][k]=dp[i+1][k]*(k+1)+dp[i+1][k+1]
    说明,因为 i 前面有 k 个数未被当做左起点使用,全部操作都只能把 a[i]更新到 h-1 这个
    高度,那么 i 号点必须自己作为某区间的左起点更新一次,在更新这个区间的同时把自己的高
    度也更新 1,达到 h。
    这样,对于下一个数 i+1 而言,算上 i 号点,它左侧有 k+1 个点可选做区间左端点,任
    选一个选后剩下 k 个点,状态 dp[i+1][k]
    若不用 i 作为区间左端点,则方案数为 dp[i+1][k+1]
    时间复杂度 O(n
    2
    ),实现时采用记忆化搜索比较方便。
     
     
     
    code;
    //
    #include<bits/stdc++.h>
    #define mod 1000000007
    using namespace std;
    #define ll long long 
    ll n,h;
    ll f[4001][4001];
    ll a[4002];
    ll  dfs(ll i,ll k)
    {
        if(f[i][k]) return f[i][k];
        {
            if(i==n+1&&k>0)
            return 0;
            if(k+a[i]==h)
                 {
                     f[i][k]=dfs(i+1,k-1)*k%mod+dfs(i+1,k)%mod;
                 }
                 if(k+a[i]+1==h)
                 {
                     f[i][k]=dfs(i+1,k)*(k+1)%mod+dfs(i+1,k+1)%mod;
                 }
        }
        return f[i][k]%mod;
        
    }
    int main()
    {
        cin>>n>>h;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
         } 
         f[n+1][0]=1;
        cout<<dfs(1,0)%mod;
        
    }

     认真看题 DP 方程自题出!!!


     
    刀剑映出了战士的心。而我的心,漆黑且残破
  • 相关阅读:
    知识点:synchronized 原理分析
    知识点:spring 完全手册
    知识点:图说 Mysql 权限管理
    知识点:Mysql 基本用法之流程控制
    知识点:Mysql 基本用法之函数
    知识点:Mysql 基本用法之存储过程
    知识点:Mysql 基本用法之事务
    知识点:Mysql 基本用法之触发器
    知识点:Mysql 基本用法之视图
    知识点:MySQL表名不区分大小写的设置方法
  • 原文地址:https://www.cnblogs.com/OIEREDSION/p/11210858.html
Copyright © 2020-2023  润新知