DP专题集

本文解析了一道关于动态规划的题目,通过实例探讨如何利用dp数组计算从给定序列中移除指定元素后,不同序列的数量,特别关注了处理重复元素的方法。解题过程中涉及状态转移方程和重复序列的排除技巧,适合动态规划初学者提升思维能力。
摘要由CSDN通过智能技术生成

1.题目描述

题目描述
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
备注:

  • 1 ≤ n ≤ 1e5
  • 1 ≤ m ≤ min{n - 1, 10}
  • 1 ≤ k ≤ 10
  • 1 ≤ si ≤ k
  • The sum of n does not exceed 1e6.

2.AC代码

先贴代码:

#include <bits/stdc++.h>
using namespace std;
#define fer(i,a,b) for(int i = a ; i <= b ; ++ i)
#define der(i,a,b) for(int i = a ; i >= b ; -- i)
#define all(x) (x).begin(),(x).end()
#define de(x) cout << x << "\n" 
#define sf(x) scanf("%lld",&x)
#define pll pair<int,int> 
//#define re register int
#define int long long 
#define pb push_back
#define y second 
#define x first 
const int N=1e5+10;
const int mod=1e9+7;
int a[N];
int dp[N][15];
signed main()
{
    int n,m,k;
    while(~scanf("%lld%lld%lld",&n,&m,&k))
    {
        memset(dp,0,sizeof(dp));
        fer(i,0,n)
            dp[i][0]=1;
        fer(i,1,n)
            sf(a[i]);
        fer(i,1,n)
        {
            fer(j,1,m)
            {
                int pos=0;
                dp[i][j]=(dp[i-1][j]+dp[i-1][j-1])%mod;
                der(kk,i-1,1)
                {
                    if(a[i]==a[kk])
                    {
                        pos=kk;
                        break;
                    }
                }
                if(i-pos<=j&&pos>=1) dp[i][j]=(dp[i][j]-dp[pos-1][j-(i-pos)]+mod)%mod;
            }
        }
        de(dp[n][m]);
        //printf("%lld\n",dp[n][m]%mod);
    }
   return 0;
}

3.个人看法

dp题写的不熟,第一次写这道题时没写出来。参考了一些大佬的题解,算是弄懂了里面的玄机。我们把dp[i][j]当做从前i个元素里删除j个元素后的不同序列的个数,那么答案就是dp[n][m]。然后我们仔细思考就可以得到这样的表达式:dp[i][j]=dp[i-1][j]+dp[i-1][j-1],然而我们再仔细思考就会发现我们的方程遗漏了重复元素,还要再减去重复元素的个数。比如,1,2,4,5,6,7,4,我们删除4,5,6,7和删除5,6,7,4后的序列都是1,2,4,就有了重复序列,我们要减去这个序列的个数。在这里就要开动你的脑筋了,想想这些产生这些重复序列的共同点了,共同点就是,两个相同元素之间的所有元素都会被删除,所以产生重复序列的前提条件就是j>=i-pos,还有就是要有相同元素,保证pos>=1就行,j为要删除元素的个数,pos为前面第一个与a[i]等值的下标,故要减掉dp[pos-1][j-(i-pos)].在实际的代码书写当中,我们每次都只选取前面第一个,这时,可能就有人提出疑问了,相同元素如果有三个以上时该怎么办呢?只选前面第一个元素的下标,那第二个第三个怎么办呢?这是个好问题,我们举个例子吧。1,2,4,5,6,4,2,4,我们就选取第一个4和第三个4举例吧,按照前面所讲,两个4之间的元素都删除,序列就为1,2,4,你会惊人的发现这个序列就是第一个4和第二个4删除之后的序列!所以,这里我们就不需要考虑重复元素了,前面已经帮我们删除了,我们直接用就是。
总的来说这道dp题的设计很巧妙,除了要得出一般的状态转移方程外,还要减去重复元素的序列,这对做题人的思维缜密有比较高的要求,而且在实际写代码的过程中,对多个重复元素的处理方法的巧妙也是让人叹为观止,这就是长期刷题才培养出来的做题思维啊!

4.原题链接

https://ac.nowcoder.com/acm/contest/20322/E

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值