zoukankan      html  css  js  c++  java
  • CodeForces 1204E"Natasha, Sasha and the Prefix Sums"(动态规划 or 组合数学--卡特兰数的应用)

    传送门

    •参考资料

      [1]:CF1204E Natasha, Sasha and the Prefix Sums(动态规划+组合数)

    •题意

      由 n 个 和 个 -1 组成的 $C_{n+m}^{n}$ 个序列;

      对所有序列的最大前缀和求和;

      并规定最大前缀和最小是 0;

    •题解

      定义 $(i,j)$ 表示序列由 i 个 1,j 个 -1 组成;

      $(i,j)$ 共有 $C_{i+j}^{i}$ 种不同的组合方式;

      $(i-1,j)$ 共有 $C_{i+j-1}^{i-1}$ 种不同的组合方式;

      $(i,j-1)$ 共有 $C_{i+j-1}^{i}$ 种不同的组合方式;

      如果同时在 $(i-1,j)$ 的 $C_{i+j-1}^{i-1}$ 和 $(i,j-1)$ 的 $C_{i+j-1}^{i}$ 种组合方式的末尾或开头分别插入 1 或 -1;

      那便是 $(i,j)$ 的不同的组合方式的种类数,即 $C_{i+j}^{i}=C_{i+j-1}^{i-1}+C_{i+j-1}^{i}$;

      根据 n,m 的范围($leq 2000$),考虑用 DP 解决这道题目;

      首先,定义 $dp[i][j]$ 表示由 $(i,j)$ 组成的 $C_{i+j}^{i}$ 个序列,对所有序列的最大前缀和求和后的结果;

      有上述前置知识,很容易想到 $(i,j)$ 可由 $(i-1,j)$ 和 $(i,j-1)$ 得到;

      这也就是说,$dp[i][j]$ 可由 $dp[i-1][j]$ 和 $dp[i][j-1]$ 转移过来;

      因为 $(i,j)$ 可由 $(i-1,j)$ 和 $(i,j-1)$ 的末尾或开头插入 1 或 -1 得到,那到底是在开头插入还是结尾插入呢?

      因为题意让求的是前缀最大值之和,所以,我们考虑到在开头插入 1 或 -1:

      • 在 $(i-1,j)$ 的开头插入 1,也就意味着这 $C_{i+j-1}^{i-1}$ 个序列的前缀最大值都会增加 1,那么
        • $dp[i][j] += dp[i-1][j]+C_{i+j-1}^{i-1}$
      • 在 $(i,j-1)$ 的开头插入 -1,意味着这 $C_{i+j-1}^{i}$ 个序列的前缀最大值会减少 1,那么
        • $dp[i][j] += dp[i][j-1]-C_{i+j-1}^{i}+h[i][j-1]$

      

      $h[i][j-1]$ 是干啥用的呢?

      由题意,前缀最大值最小为 0,所以,在 $(i,j-1)$ 的开头插入 -1 的时候,前缀最大值为 0 的序列是不会减少 1 的;

      我们就需要将这些多减掉的 1 在加上;

      定义 $h[i][j]$ 表示 $(i,j)$ 的 $C_{i+j}^{i}$ 个序列种前缀最大值为 0 的个数;

      同样 $h[i][j]$ 可由 $h[i-1][j]$ 和 $h[i][j-1]$ 转移过来;

      考虑到 $h[i][j]$ 的定义,我们这次选择在 $(i-1,j)$ 和 $(i,j-1)$ 的结尾插入 1 或 -1;

      很容易想到,如果 $i > j$,一定有 $h[i][j]=0$,所以,我们考虑 $i le j$ 的情况;

      因为是在结尾插入的,所以,前缀最大值第一次出现的位置是不会改变的,所以有 $h[i][j]=h[i-1][j]+h[i][j-1]$;

    •Code

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 #define mem(a,b) memset(a,b,sizeof(a))
     5 const int maxn=2e3+50;
     6 const int MOD=998244853;
     7 
     8 int n,m;
     9 ll dp[maxn][maxn];
    10 ll h[maxn][maxn];
    11 ll C[2*maxn][2*maxn];
    12 
    13 void Init()
    14 {
    15     C[0][0]=1;
    16     for(int i=1;i < 2*maxn;++i)
    17         for(int j=0;j <= i;++j)
    18         {
    19             if(j == 0 || j == i)
    20                 C[i][j]=1;
    21             else
    22                 C[i][j]=C[i-1][j]+C[i-1][j-1];
    23             C[i][j] %= MOD;
    24         }
    25         
    26     mem(h,0);
    27     for(int j=0;j < maxn;++j)
    28         h[0][j]=1;
    29     for(int i=1;i < maxn;++i)
    30         for(int j=i;j < maxn;++j)
    31             h[i][j]=(h[i-1][j]+h[i][j-1])%MOD;
    32 
    33 
    34     dp[0][0]=0;
    35     for(int i=1;i < maxn;++i)
    36         dp[i][0]=i;
    37     for(int j=1;j < maxn;++j)
    38         dp[0][j]=0;
    39     for(int i=1;i < maxn;++i)
    40         for(int j=1;j < maxn;++j)
    41         {
    42             dp[i][j]=(dp[i-1][j]+C[i+j-1][j])+(dp[i][j-1]-C[i+j-1][i]+h[i][j-1]);
    43             dp[i][j]=(dp[i][j]+MOD)%MOD;
    44         }
    45 }
    46 
    47 int main()
    48 {
    49     Init();
    50     scanf("%d%d",&n,&m);
    51     printf("%lld
    ",dp[n][m]);
    52 
    53     return 0;
    54 }
    View Code
  • 相关阅读:
    取消PHPCMS V9后台新版本升级提示信息
    phpcmsv9全站搜索,不限模型
    jq瀑布流代码
    phpcms v9模版调用代码
    angular.js添加自定义服务依赖项方法
    angular多页面切换传递参数
    angular路由最基本的实例---简单易懂
    作用域事件传播
    利用angular控制元素的显示和隐藏
    利用angular给节点添加样式
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/11718617.html
Copyright © 2011-2022 走看看