序列【DP】

给定一个整数n和长度k,找出所有序列,满足序列中每个数都是前一个数的倍数,且序列长度为k,所有数不大于n。通过动态规划方法,计算符合条件的序列数量,结果对1000000007取模。对于40%的数据,1≤N≤30,1≤k≤10;对于100%的数据,1≤N≤2000,1≤k≤2000。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

>Description
一个长度为k的整数序列,后一个数是前一个数的倍数。且每个数小于等于n。
给定N和k,请算出有多少个长度为k的这种序列,答案对1000000007取模。


>Input
输入共1行,包含2个用空格隔开的整数N和k。

>Output
输出共1行,包含一个整数,表示长度为k的“好序列”的个数对1000000007取模后的结果。


>Sample Input
3 2

>Sample Output
5

【数据说明】
对于40%的数据,1≤N≤30,1≤k≤10。
对于100%的数据,1≤N≤2000,1≤k≤2000。


解题思路
一开始看这道题的时候试图找到规律,但是在草稿纸上算了很久都没有算出来,就放弃了。

后来看了一下正解,发现超级简单,用DP就可以了:
f[i][j]表示:有i位数,第i位数为j,k为j的倍数,f[i+1][j*k]+=f[i][j]


>代码

#include<iostream>
#include<cstdio>
using namespace std;
int n,k,f[2005][2005],ans;
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) f[1][i]=1; //设初值
	for(int i=1;i<k;i++) //位数
	 for(int j=1;j<=n;j++) //第i位数为j
	  for(int k=1;k<=n/j;k++) //倍数
	   f[i+1][j*k]=(f[i+1][j*k]+f[i][j])%1000000007;
	for(int i=1;i<=n;i++)
	 ans=(ans+f[k][i])%1000000007;
	printf("%d",ans);
	return 0;
}
### 子序列动态规划算法详解 #### 定义与特性 动态规划(Dynamic Programming, DP)是一种通过将复杂问题分解为更小的子问题来解的方法,特别适用于具有最优子结构和重叠子问题性质的问题[^3]。对于最长公共子序列(Longest Common Subsequence, LCS)这类问题,动态规划能够有效地找到两个或多个字符串之间的最长共同部分。 #### 最长公共子序列 (LCS) 的定义 给定两个序列 X 和 Y,如果存在一个严格递增下标的整数序列 i_1 < i_2 < ... < i_k 及 j_1 < j_2 < ... < j_k 使得 Xi_{m} = Yj_{m}, 则称此序列为X和Y的一个公共子序列。而其中长度最大的称为这两个序列的最长公共子序列[LCS][^1]. #### 动态规划解决 LCS 问题的过程 为了利用动态规划解决问题,需要构建一张二维表格 c[i,j], 表示Xi到第i个字符为止,Yj到第j个字符为止的最大匹配数目: - 当 `X[i]=Y[j]` 时,则有 `c[i][j]=c[i−1][j−1]+1`; - 否则取较大者作为当前格子的结果:`max(c[i−1][j],c[i][j−1])`. 最终答案即为表中的最后一个元素值 c[m,n]. 这样做不仅解决了问题本身还记录下了路径以便回溯打印具体的方案. ```cpp #include<iostream> using namespace std; void lcs( char *X ,char *Y,int m,int n ) { int L[m+1][n+1]; for(int i=0;i<=m;i++) for(int j=0;j<=n;j++){ if(i==0 || j==0) L[i][j]=0; else if(X[i-1]==Y[j-1]) L[i][j]=L[i-1][j-1]+1; else L[i][j]=max(L[i-1][j],L[i][j-1]); } } ``` 上述代码展示了如何使用 C++ 来实现寻找两串字符间的最大公共子序列的功能[^2]. #### 关键概念解释 - **最优子结构性质**: 如果一个问题可以被分割成几个较小规模相同类型的子问题,并且这些子问题之间可能存在依赖关系,那么这个问题就具备了所谓的“最优子结构性质”。这意味着可以通过组合各个子问题的最佳解决方案来获得整个问题的整体最佳解答。 - **重叠子问题属性**: 在某些情况下,在尝试用递归来解决问题的过程中可能会遇到相同的子问题反复出现的情况。为了避免不必要的重复工作,我们可以采用记忆化技术或者自底向上的迭代方式预先存储那些已经被处理过的小型实例的答案,以此加快整体运算速度并减少资源消耗。 - **状态转移方程**: 描述了从已知条件出发逐步推导未知情况变化规律的一种表达形式。它明确了每一个阶段的状态是如何从前一阶段演变而来以及两者间存在着怎样的联系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值