蓝桥杯:等差数列(求最短数列长度)动态规划解法

蓝桥杯:等差数列(求最短数列长度)动态规划解法

好像没有必要用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;
}


发布了50 篇原创文章 · 获赞 2 · 访问量 3075

猜你喜欢

转载自blog.csdn.net/weixin_44176696/article/details/104087141