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