动态规划-----牛牛与数组

动态规划------牛牛与数组

问题描述:
链接:https://ac.nowcoder.com/acm/problem/21738
来源:牛客网

牛牛喜欢这样的数组:
1:长度为n
2:每一个数都在1到k之间
3:对于任意连续的两个数A,B,A<=B 与(A % B != 0) 两个条件至少成立一个

请问一共有多少满足条件的数组,对1e9+7取模

输入描述:
输入两个整数n,k
1 ≤ n ≤ 10
1 ≤ k ≤ 100000

输出描述:
输出一个整数

示例1
输入
2 2
输出
3
示例2
输入
9 1
输出
1
示例3
输入
3 3
输出
15
示例4
输入
2 1234
输出
1515011

思路分析

根据题目的描述,可以很容易的分析出这是一道动态规划类的题目(因为当我们求长度为n的数组时,
我们需要先求出前(n-1)个数组元素的情况,符合动态规划的最优子结构问题)
首先,当 n = 1时,根据题意可知有 一共有 k 种情况
	 当 k = 1 时, 只有 1 种情况
其次,n != 1 && k != 1时
	本题我们选取二维数组进行存储数据,dp[i][j]表示数组的第 i 个	元素取值为j ,其中储存的数据为当前情况满足题意的总数;
根据题意分析可知,当a[i] >= a[i - 1](a代表这个数组)时,所有情况都满足题意, 
而当a[i] < a[i - 1]时,在a[i - 1] % a[i] != 0时也是满足题意的,
综上所述,a[i]的取值应该是从0 到 k, 减去a[i - 1] % a[i] != 0时的取值即可(**如代码中的111之间的部分**),
所以,dp[i][j]  = dp[i - 1][m](1<= m <= k) - dp[i - 1][p](p是j的倍数);

完整代码

#include<iostream>
using namespace std;
int main() {
	int n, k;
	cin>>n>>k;
	int dp[n + 1][k + 1], rec, M = 1e9 + 7, r, ans = 1;
	//当n = 1 时,一共有k总情况,所以dp[1][j]都是1
	for (int i = 1; i <= k; i++) {
		dp[1][i] = 1;
	}
	for (int i = 2; i <= n; i++) {
		rec = 0;
		dp[i][1] = 1;
		//  111
		for (int j = 1; j <= k; j++) { 
		//rec是求dp[i - 1][j](1<= j <= k)的总和
			rec += dp[i - 1][j];
			rec %= M;
		}
		for (int j = 2; j <= k; j++) {
			r = 0;
			for (int m = j * 2; m <= k; m += j) {
			//r是当a[i]小于a[i - 1]时,a[i - 1] % a[i] == 0 时的总和,这个需要减去
				r += dp[i - 1][m];
				r %= M;
			}
			dp[i][j] = (rec - r) % M;
			if (i == n) {
			//此处是用于求总数, 注意,此处的ans的初始值为1,因为外层循环的j是从2开始,当j=1时,dp(i)(j)的值为1
				ans = (ans + dp[i][j]) % M;
			}
		}
		//111
	}
	if (n == 1) {
		cout<<k;
	} else {
			cout<<ans;
	}
	return 0;
}

总结

在做动态规划的问题时,一定要首先分析问题的递推关系式,这个是解题的关键,也是难点;其次,在数组的选择时,这个要多做题进行总结,在我看来,动态规划有种用空间来降低时间效率的感觉(暂时这么感觉,毕竟只学到皮毛)
对于新手(我就是新手,这道题就是我分析他人的代码,写的解析),刚开始接触时是非常痛苦的,因为动态规划确实很难,在刚开始时,可以多分析他人的代码,总结经验,多做题,总会有提高的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值