牛客网暑期ACM多校训练营(第一场) E Removal dp 动态规划 详解

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld

题目描述

Bobo has a sequence of integers s1, s2, …, sn where 1 ≤ si ≤ k.
Find out the number of distinct sequences modulo (109+7) after removing exactly m elements.

输入描述:

The input consists of several test cases and is terminated by end-of-file.
The first line of each test case contains three integers n, m and k.
The second line contains n integers s1, s2, ..., sn.

输出描述:

For each test case, print an integer which denotes the result.

输入例子:

3 2 2
1 2 1
4 2 2
1 2 1 2

输出例子:

2
4


int dp[M][20];//前i个数字删除j个后有多少种结果

转移方程:dp[i][j]=dp[i-1][j-1](第i个数字不删)+dp[i-1][j](第i个数字删) (当前值=左上角+正上方的数)

 但这样计算出的dp[][]值是存在重复情况的

         引用:考虑{…, 1, 2, 3, 1, 2}删除3个数字,{…, 1, 2, 3, 1, 2}({…, 1, 2})和{…, 1, 2, 3, 1, 2}是一样的({…, 1, 2}),也就是说删除两个相同数字之间的所有数字+这两个数字中的一个会有一次重复计数,需要减去,在例子中{…, 1, 2}被计数了两次,要减去一次(即减去{…}的次数),令pre[i] := s[i]前面第一个和s[i]相同的元素位置, 则dp[i][j]应该减去dp[pre[i]-1][j-(i-pre[i])]

       引用:   比如:cdeaae
当我们i=6,j=3时,a[3]=a[6],那么如果删掉中间的[eaa]字串就会变成[cde],
因为我们已经在前面i=3时算过了一次[cde] dp[3][0]这种情况,所以我们需要dp[6][3]-dp[2][0]。
那么为什么不是减掉dp[3][0]而是减掉dp[2][0]呢。
举个比较好说明的例子。  
还是上面那个串  cdeaae  ,假设现在是i=6,j=4 ( dp[6][4] ce 重复)

那么我们需要dp[6][4]-dp[2][1] == dp[6][4]-dp[3][1]+dp[2][0];。

(dp[2][1]=dp[1][1]+dp[1][0];)

那么为什么不是减掉dp[3][1]呢。
因为dp[3][1]=dp[2][1]+dp[2][0],也就是说dp[3][1]还包括了删掉 a[3](cd)的状态,
而如果删掉a[3],那么加入a[6]的时候就不会有重复了。所以减掉dp[2][1],就是需要 减掉 a[3]不删除的情况,即dp[2][0];

int pre[M];//前一个与相等 i 值的位置//若前面没有与i相同的数 pre[i]值则为0
int prex[M];// i值 的位置// 若前面有与i相同的数 prex[i]值则表示最后位置
int s[M];//原序列数组

代码:

#include<bits/stdc++.h>
using namespace std;
const int  M=100010;
const int mod=1e9+7;
int dp[M][20];//前i个数字删除j个后有多少种结果
int pre[M];//前一个与相等 i 值的位置//若前面没有与i相同的数 pre[i]值则为0
int prex[M];// i值 的位置// 若前面有与i相同的数 prex[i]值则表示最后位置
int s[M];//原序列数组
int main()
{
  int i,j,n,m,k;
  while(cin>>n>>m>>k)
  {
    for(i=0; i<=n+2; i++)//初始化
    {
      memset(dp[i],0,sizeof dp[i]);
      pre[i]=0;
    }
    memset(prex,0,sizeof prex);
    for(i=1; i<=n; i++)
    {
      cin>>s[i];
      pre[i]=prex[s[i]];//前一个与s[i]值相等的位置
      prex[s[i]]=i;//记录s[i]值当前的位置
    }
    int ans=0;
    dp[0][0]=1;
    for(i=1; i<=n; i++)
    {
      dp[i][0]=1;
      int d=i-pre[i];//与前面相同的s[i] 位置之间的相隔距离d;
      for(j=1; j<=m&&j<=i; j++)
      {
        dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%mod;// 左上角+正上方的数值 从低递推
        if(pre[i]&&d<=j)
        {
          dp[i][j]=(dp[i][j]-dp[pre[i]-1][j-d]+mod)%mod;
           //减去刚好重复的那部分
        }
      }
      //输出dp[][]数组 会比较直面
//      printf("%d\n",i);
//      for(int o=1;o<=n;o++)
//      {
//          for(int p=1;p<=n;p++)
//          {
//              printf("%d\t",dp[o][p]);
//          }puts("");
//      }
    }
    printf("%d\n",dp[n][m]);
  }
}

猜你喜欢

转载自blog.csdn.net/qq_41668093/article/details/81186140