DP专题集

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

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

Delphi 12.3 作为一款面向 Windows 平台的成开发环境,由 Embarcadero Technologies 负责其持续演进。该环境以 Object Pascal 语言为核心,并依托 Visual Component Library(VCL)框架,广泛应用于各类桌面软件、数据库系统及企业级解决方案的开发。在此生态中,Excel4Delphi 作为一个重要的社区开源项目,致力于搭建 Delphi 与 Microsoft Excel 之间的高效桥梁,使开发者能够在自研程序中直接调用 Excel 的文档处理、工作表管理、单元格操作及宏执行等功能。 该项目以库文件与组件包的形式提供,开发者将其成至 Delphi 工程后,即可通过封装良好的接口实现对 Excel 的编程控制。具体功能涵盖创建与编辑工作簿、格式化单元格、批量导入导出数据,乃至执行内置公式与宏指令等高级操作。这一机制显著降低了在财务分析、报表自动生成、数据整理等场景中实现 Excel 功能成的技术门槛,使开发者无需深入掌握 COM 编程或 Excel 底层 API 即可完成复杂任务。 使用 Excel4Delphi 需具备基础的 Delphi 编程知识,并对 Excel 对象模型有一定理解。实践中需注意不同 Excel 版本间的兼容性,并严格遵循项目文档进行环境配置与依赖部署。此外,操作过程中应遵循文件访问的最佳实践,例如确保目标文件未被独占锁定,并实施完整的异常处理机制,以防数据损毁或程序意外中断。 该项目的持续维护依赖于 Delphi 开发者社区的体贡献,通过定期更新以适配新版开发环境与 Office 套件,并修复已发现的问题。对于需要深度融合 Excel 功能的 Delphi 应用而言,Excel4Delphi 提供了经过充分测试的可靠代码基础,使开发团队能更专注于业务逻辑与用户体验的优化,从而提升整体开发效率与软件质量。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值