map+DP leetcode446

 

如果数字序列由至少三个元素组成并且任何两个连续元素之间的差异相同,则称为算术序列。

例如,这些是算术序列:

1,3,5,7,9 
7,7,7,7 
3,-1,-5,-9 
以下序列是不算术。

1, 1, 2, 5, 7

给出了由N个数组成的零索引数组A. 该阵列的子序列切片是任何整数序列(P0,P1,...,Pk),使得0≤P0<P1 <... <Pk <N。

如果序列A [P0],A [P1],...,A [Pk-1],A [Pk]是算术的,则阵列A的子序列片(P0,P1,...,Pk)被称为算术。特别是,这意味着k≥2。

该函数应返回数组A中的算术子序列片数。

输入包含N个整数。每个整数的范围为-231和231-1,0≤N≤1000。输出保证小于231-1。

例:

输入:[2,4,6,8,10]

输出:7

说明:
所有算术子序列切片为:
[2,4,6] 
[4,6,8] 
[6,8,10] 
[2,4,6,8] 
[4,6,8,10] 
[2,4,6,8,10] 
[2,6,10]

 

 

简单的子序列这种题,一般思路先想到的dp就是设置以第i个元素结尾的一种状态,然后能推到最后吧?

比如最长递增子序列这种的题。

 

那我们就试试:dp[i]代表以第i个元素结尾的等差数列的个数。

然后我们想一下状态转移方程是什么?

我们会发现,挺难推出来的。。因为对于本个元素,最容易想的思路就是把前面的结尾都遍历一遍,然后能组成等差数列的都加起来吧?

但是我们无法判断之前的结尾组成的等差数列中,加上第i个元素还能不能是等差数列。。。

比如第5个数是5,能和前面组成1,2,3,4,5和2,3,4,5和3,4,5和1,3,5这几个序列,那dp[4]应该是4咯,我们对于后面的结尾,比如数字7,算dp时,遍历到dp[4]了,我们无法判断dp[4]里这四个序列有几个公差为7-5=2(也就是能组成)。

我之前写过,动态规划就是空间优化时间的算法。

我们推不出来,因为信息不全。需要记录更多的信息。我们不止要知道dp[i]是多少,还要知道每个公差的序列有多少,才能推出来之后的dp序列。

我们可以二维dp[a][b]表示第a个元素结尾公差为b的序列个数,但是由于数据较为稀疏,浪费空间时间较大,我们可以map走一波。

比如上面那个例子,我们要记录:dp[4],公差为1的序列有仨,公差为2的有一个。

这样,我们之后遇到了6,那6-5=1,dp[4]里有三个序列公差为1,我们就能知道这三个序列加上6还是等差的。

遇到7,也能7-5=2,找dp[4]中公差为2的即可。

 

还有一个需要注意的点:题目说长度最小为3才算。

过程叙述:

计算两个数字之差diff,如果越界了不做处理

如果没越界,dp[i]中diff的映射增1,然后看dp[j]中是否有diff的映射

如果有的话,说明此时已经能构成等差数列了(三个数),将dp[j][d]加入结果res中,然后再更新dp[i][d]

这样等遍历完数组,res即为总数。

#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <climits>
#include <algorithm>
#include <sstream>
#include <functional>
#include <bitset>
#include <cmath>

using namespace std;


class Solution 
{
public:
    int numberOfArithmeticSlices(vector<int>& A) 
    {
        if (A.size() <= 2)
            return 0;

        int count = 0;
        vector<map<int, int>> dp(A.size());
        for (int i = 0; i < A.size(); i++)
        {
            for (int j = 0; j < i; j++)
            {
                if ((long)A[i] - (long)A[j] > INT_MAX || (long)A[i] - (long)A[j] < INT_MIN) 
                    continue;//节省时间
                int diff = A[i] - A[j];//公差
                dp[i][diff] += 1;
                if (dp[j].find(diff) != dp[j].end()) //能构成至少三个数等差
                {
                    dp[i][diff] += dp[j][diff];
                    count += dp[j][diff];
                }
            }
        }
        return count;
    }
};


 

 

 

 

 

 

 

  • 233
    点赞
  • 200
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 25
    评论
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兔老大RabbitMQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值