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

https://www.nowcoder.com/acm/contest/139/E
解法一:
dp[i][j] d p [ i ] [ j ] 表示加入第i个数字后,总共删掉 j j 个数字时,有多少种不同的序列。
不考虑重复的dp方程为:dp[i][j]=dp[i1][j]+dp[i1][j1]
那么在这里减去重复的数据就是正确的答案。
例如
acdijkc a c d i j k c
其中重复的两个状态就是 ac a c ,其实可以判断出,重复的情况必定在存在相同字母时出现。我们可以保存一下每一个字母之前字母出现的位置,当中间的字母全被删掉时,两者就会重复。即 dp[i][j]=dp[pre[i]1][j(ipre[i])]; d p [ i ] [ j ] − = d p [ p r e [ i ] − 1 ] [ j − ( i − p r e [ i ] ) ] ; 为什么多减了一位呢?请观察 dp[i][j] d p [ i ] [ j ] .

#include<bits/stdc++.h>
using namespace std;
#define ll long long int
const int maxn=1e5+25;
const int p=1e9+7;
ll dp[maxn][15];
int a[maxn];
int pre[maxn];
int vis[15];
int main()
{
    int n,m,k;
    while(~scanf("%d %d %d",&n,&m,&k)){
        memset(vis,0,sizeof vis);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        for(int i=0;i<=n;i++){pre[i]=0;}
        for(int i=0;i<=n;i++){
            for(int j=0;j<=m;j++){
                dp[i][j]=0;
            }
        }
        for(int i=1;i<=n;i++){
            if(!vis[a[i]]){
                vis[a[i]]=i;
            }
            else{
                pre[i]=vis[a[i]];
                vis[a[i]]=i;
            }
        }
        for(int i=0;i<=n;i++) dp[i][0]=1;

        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                (dp[i][j]=dp[i-1][j]+dp[i-1][j-1])%=p;
                if(pre[i]){
                    if(i-pre[i]<=j){
                        dp[i][j]-=dp[pre[i]-1][j-(i-pre[i])];
                        (dp[i][j]+=p)%=p;
                    }
                }
            }
        }
        printf("%lld\n",dp[n][m]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值