蓝桥杯:等差数列(求最短数列长度)动态规划解法
好像没有必要用dp,但是一看到题感觉可以用,姑且写一个吧,后来看到大佬写的,是求公约数直接解的,dp还是复杂了
问题描述
数学老师给小明出了一道等差数列求和的题目。但是粗心的小明忘记了一部分的数列,只记得其中 N 个整数。现在给出这 N 个整数,小明想知道包含这 N 个整数的最短的等差数列有几项?
输入格式
输入的第一行包含一个整数 N。
第二行包含 N 个整数 A1; A2; · · · ; AN。 (注意 A1 ∼ AN 并不一定是按等差数列中的顺序给出)
输出格式
输出一个整数表示答案。
样例输入
5
2 6 4 10 20
样例输出
10
样例说明
包含 2、 6、 4、 10、 20 的最短的等差数列是 2、 4、 6、 8、10、 12、 14、 16、18、 20。
评测用例规模与约定
对于所有评测用例, 2 ≤ N ≤ 100000, 0 ≤ Ai ≤ 109
思路
-
假设数列的前2项是 2,4,那么可以组成公差为1的等差数列的长度为 3(即 2,3,4),可以组成公差为2的等差数列的长度为 2(即 2,4)
-
如果第三项是6,6 - 4 = 2 那么可能的公差就是0,1,2
1.前两项无法组成公差为0的等差数列,故不考虑
2.前三项组成公差为1的数列的长度 = 前两项组成公差为1数列的长度 + (6 - 4) / 1 = 3 + 2 = 5
3.前三项组成公差为2的数列的长度 = 前两项组成公差为2数列的长度 + (6 - 4) / 2 = 2 + 1 = 3
-
按照上述规则遍历所有可能的公差,即可求取最短值
状态转移:
// nums[]数组是排序过后的输入数据
// dp[i][j]表示前i个数字可以组成公差为j的数列的长度
dp[i][j] = dp[i-1][j] + (nums[i]-nums[i-1])/j
代码
由于没有后台数据,不确定是否完全正确,仅供参考
实际OJ可能得用一维数组优化(dp第i行只用到第i-1行的值),否则可能空间超限
可能还得时间优化一下,不穷举所有的公差,而是穷举从0到Nums[i]-Nums[i-1] 的所有的数字
#include <iostream>
using namespace std;
#define MAXLEN 100009
#define MAXDELTA 1009
// dp[i][j]表示前i个数字可以组成公差为j的数列的长度
int dp[MAXLEN][MAXDELTA];
int nums[MAXLEN];
int n, m;
int main()
{
cin>>n;
for(int i=1; i<=n; i++)
{
cin>>nums[i];
}
// 排序
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n-1; j++)
{
if(nums[j] > nums[j+1])
{
int temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
}
}
}
// 找最大差(不一定是公差,但是是公差的上限)
m = 0;
for(int i=2; i<+n; i++)
{
if(nums[i+1]-nums[i] > m)
{
m = nums[i+1]-nums[i];
}
}
// 初始化dp数组
for(int i=1; i<=n; i++)
{
for(int j=0; j<=m; j++)
{
dp[i][j] = -1;
}
}
// 计算前两个数能够组成公差为j的数列的长度
for(int j=0; j<=m; j++)
{
if(nums[2]-nums[1] == 0)
{
dp[2][0] = 2;
}
else if(j!=0 && (nums[2]-nums[1])%j==0)
{
dp[2][j] = (nums[2]-nums[1])/j + 1;
}
}
// dp 其实这里状态只用到了上一行,可以用一维数组优化
for(int i=3; i<=n; i++)
{
for(int j=0; j<=m; j++)
{
if(nums[i]-nums[i-1] == 0)
{
dp[i][0] = dp[i-1][0] + 1;
}
else if(j!=0 && (nums[i]-nums[i-1])%j==0 && dp[i-1][j]>0)
{
dp[i][j] = dp[i-1][j] + (nums[i]-nums[i-1])/j;
}
}
}
// 找最小
int min = 1145141919;
for(int j=0; j<=m; j++)
{
if(dp[n][j]<min && dp[n][j]>0)
{
min = dp[n][j];
}
}
cout<<min<<endl;
return 0;
}