zoukankan      html  css  js  c++  java
  • 2018牛客暑假多校一 E(dp)

    题目描述:

        给你一个长度为n的数列(n<=1e5),数列只会有k(1<=k<=10)种数字。问你如果你可以在这个数列种删除m个数(max(m)=10),问你可以获得的不同的数列的个数为多少。

    题目分析:

        可以分析,因为在一个长度为n的数列种,删除m个数,倘若只让我们求方案数,那么我们直接可以运用dp[i][j],使得dp代表前i个的数列中删除j个数所形成的方案数,并列出转移方程,dp[i][j]=dp[i-1][j]+dp[i-1][j-1];

        但是,因为这个题目中是要统计不相同的数列的个数,而因为倘若前面有与a[i]相同的数字a[k] (k<i),并且i与k的位置距离小于等于j,那么就会产生重复。因此,我们之前所求出的方案数是有很多重复的。

        因此,我们还需要去判断当前的数字a[i]在前面是否出现过,倘若在之前出现过a[k],且两者之间的距离正好小于等于可以删除的个数,则我们需要减去这部分重复的方案数。此时的转移方程为 dp[i][j]=dp[i][j]-dp[pre[i]-1][j-(i-pre[i])];

    #include <bits/stdc++.h>
    #define maxn 100005
    using namespace std;
    typedef long long ll;
    const int mod=1e9+7;
    ll a[maxn];
    ll dp[maxn][20];
    int pre[maxn];
    int id[maxn];
    int main()
    {
        int n,m,k;
        while(~scanf("%d%d%d",&n,&m,&k)){
            memset(pre,0,sizeof(pre));
            memset(id,0,sizeof(id));
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
                pre[i]=id[a[i]];
                id[a[i]]=i;
            }
            for(int i=0;i<=m;i++){
                dp[i][i]=1;
            }
            for(int i=1;i<=n;i++){
                dp[i][0]=1;
                int d=i-pre[i];
                for(int j=1;j<=m;j++){
                    if(j>i) break;
                    dp[i][j]=((dp[i-1][j]+dp[i-1][j-1])%mod+mod)%mod;
                    if(pre[i]!=0&&d<=j){
                        dp[i][j]=((dp[i][j]-dp[pre[i]-1][j-d])%mod+mod)%mod;
                    }
                }
            }
            cout<<dp[n][m]<<endl;
        }
    }
    
  • 相关阅读:
    Django重新构造User模型
    在docker中添加mysql在通过远程机器的访问
    php基础笔记
    mysql基础笔记整理
    redis的配置安装与使用
    c++实现对两个有序链表的连接
    java的网络编程(TCP)
    无心制作
    nacos配置服务
    声明式远程调用OpenFeign(微服务调用微服务)
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11007267.html
Copyright © 2011-2022 走看看