牛客网暑期ACM多校训练营(第一场)E Removal [计数dp]

ERemoval

题目:一个数组序列,有n个数,计算删掉m个数字后不相同的序列的个数;

思路:计数dp,总之就是动态规划,我们先算不考虑重复的情况就是

状态dp[ i ][ j ]:前 i 个数,删除 j 个的方法数;

状态转移方程:dp[ i ][ j ] = dp[ i - 1][ j - 1] + dp[ i  - 1 ][ j ];//当前位置数字是否删除!

考虑重复(如下图):   

 

                         

                                        图1                                                                                   图2

从上图可以看出,重复的位置就是两个相同数字之间,那么减去就可以啦,用pre[i]记录前一个相同的数字出现的位置,然后减去这之间的数字,就是dp[ i ][ j ] - dp[ pre[i] - 1][ j - ( i - pre[i])];

代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MOD = 1e9 + 7;
const int maxn = 1e5+7;

void update(int& a, int b)
{
    a += b;
    if(a > MOD) a -= MOD;
}

int s[maxn], dp[maxn][20], pre[maxn], prex[20];

int main()
{
    int n, m, k;
    while (~scanf("%d%d%d", &n, &m, &k)) {
        memset(dp, 0, sizeof(dp));
        memset(prex, 0, sizeof(prex));
        for(int i = 1; i <= n; i++){
            scanf("%d", &s[i]);
            pre[i] = prex[s[i]];
            prex[s[i]] = i;
        }
        dp[0][0] = 1;
        for(int i = 1; i <= n; i++)
        {
            dp[i][0] = 1;
            int d = i - pre[i];
            for(int j = 1; j <= m&&j <= i; j++)
            {
                update(dp[i][j], dp[i-1][j-1] + dp[i-1][j]);
                if(pre[i]&&d<=j) update(dp[i][j] ,MOD - dp[pre[i] - 1][j - d]);
            }
        }
        printf("%d\n",dp[n][m]);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值