牛客网暑期ACM多校训练营(第一场)---E Removal

本文章参考:https://www.cnblogs.com/Fy1999/p/9354155.html

主要还是搬人家的,我这也是修修补补。

 

链接:https://www.nowcoder.com/acm/contest/139/E
来源:牛客网
 

题目描述

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.

 

示例1

输入

复制

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

输出

复制

2
4

备注:

* 1 ≤ n ≤ 105
* 1 ≤ m ≤ min{n - 1, 10}
* 1 ≤ k ≤ 10
* 1 ≤ si ≤ k
* The sum of n does not exceed 106.

题意:给一串序列,序列的每个值都小于等于k,让你从中删掉m个,问:有多少种不同的序列?

思路:

dp[i][j]表示加入第i个数字后(前i个),总共删掉j个数字时,有多少种不同的序列
假设不考虑有重复的情况,dp方程为:dp[i][j]=dp[i-1][j] (第i个数字不删)+dp[i-1][j-1] (第i个数字删)。
现在考虑重复的情况。
如果前面有与a[i]相同的数字a[k] (k小于i),并且i-k<=j,就会产生重复。

这里说明一下为什么i-k<=j,就会产生重复? 

因为只有这样,我们把相同数字之间的全部删掉,就会出现重复,例如:i=6,j=3, cdefae 会出现重复(把fa删掉+任意个e,而cedfae 就不会出现重复(不管怎么删,都不会)
比如:cdeaae(用字符串举例比较方便)
当我们i=6,j=3时,a[3]=a[6],那么如果删掉中间的[eaa]字串就会变成[cde],因为我们已经在前面i=3时算过了一次[cde]这种情况,所以我们需要dp[6][3]-dp[2][0]。
那么为什么不是减掉dp[3][0]而是减掉dp[2][0]呢。
举个比较好说明的例子。
还是上面那个串,假设现在是i=6,j=4。那么我们需要dp[6][4]-dp[2][1]。
那么为什么不是减掉dp[3][1]呢。
因为dp[3][1]=dp[2][1]+dp[2][0],也就是说dp[3][1]还包括了删掉a[3]的状态,而如果删掉a[3],那么加入a[6]的时候就不会有重复了。所以减掉dp[2][1],就是减掉了a[3]不删除的情况。

 

贴下代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

#define mod 1000000007
const int maxn=100010;

int num[maxn];
int pre[maxn],last[maxn];

int dp[maxn][20];
int main()
{
        int n,m,k;
        while(~scanf("%d%d%d",&n,&m,&k))
        {

            memset(last,0,sizeof(last));

            for(int i=1;i<=n;i++)
            {
                scanf("%d",&num[i]);
                pre[i]=last[num[i]]; ///表示在之前与当前数字相同的位置是哪个
                last[num[i]]=i; ///表示当前数字的位置
            }

            for(int i=0;i<=m;i++) dp[i][i]=1; ///表示在前i个数字中,删掉全部有多少种不同序列,
                                            ///切记从0开始初始化
            for(int i=1;i<=n;i++)
            {
                dp[i][0]=1;///表示不删掉数字

                for(int j=1;j<=min(i-1,m);j++)
                {
                    dp[i][j]=(dp[i-1][j]+dp[i-1][j-1])%mod;

                    if(pre[i]&&i-pre[i]<=j){
                        dp[i][j]-=dp[pre[i]-1][j-(i-pre[i])]; ///dp[pre[i]-1][j-(i-pre[i])]中,pre[i]-1,表示不删掉pre[i],
                        ///j-(i-pre[i]) 表示把两个相同数字之间的其它数字删掉也还不够,需要在往前删掉j-(i-pre[i])个数字才行
                        dp[i][j]%=mod;
                        dp[i][j]=(dp[i][j]+mod)%mod; ///防止负数取模
                    }
                }
            }

            printf("%d\n",dp[n][m]);


        }
        return 0;
}



 

猜你喜欢

转载自blog.csdn.net/ljd201724114126/article/details/81197331
今日推荐