413. 等差数列划分➕446. 等差数列划分 II - 子序列

413题目:

如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列。

例如,以下数列为等差数列:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
以下数列不是等差数列。

1, 1, 2, 5, 7
 

数组 A 包含 N 个数,且索引从0开始。数组 A 的一个子数组划分为数组 (P, Q),P 与 Q 是整数且满足 0<=P<Q<N 。

如果满足以下条件,则称子数组(P, Q)为等差数组:

元素 A[P], A[p + 1], ..., A[Q - 1], A[Q] 是等差的。并且 P + 1 < Q 。

函数要返回数组 A 中所有为等差数组的子数组个数。

 

示例:

A = [1, 2, 3, 4]

返回: 3, A 中有三个子等差数组: [1, 2, 3], [2, 3, 4] 以及自身 [1, 2, 3, 4]。

 

解答:

数学法:

找连续的等差数列i~j,如0~5,那么该区间能构成等差数列为(6-2)*(1+6-2)/2=10。即长度为3的:4个,长度4的:3个,长度5的:2个,长度6的:1个。

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& A) {
        int n=A.size();
        if(n<3){
            return 0;
        }
        int res=0;
        vector<bool> flag(n,false);
        for(int i=2;i<n;++i){
            if(A[i-1]-A[i-2]==A[i]-A[i-1]){
                flag[i]=true;
            }
        }
        for(int i=2;i<n;){
            if(flag[i]==false){
                ++i;
            }
            int cnt=0;
            while(i<n and flag[i]){
                ++i;
                ++cnt;
            }
            res+=(cnt+1)*cnt/2;
        }
        return res;
    }
};

动态规划:

dp[i]=dp[i-1]+1    dp[i]表示以i结尾的等差数列数量,总结果为所有dp[i]之和。

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& A) {
        int n=A.size();
        vector<int> dp(n,0);
        int res=0;
        for(int i=2;i<n;++i){
            if(A[i]-A[i-1]==A[i-1]-A[i-2]){
                dp[i]=dp[i-1]+1;
            }
            res+=dp[i];
        }
        return res;
    }
};

 

446题目:

如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列。

例如,以下数列为等差数列:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
以下数列不是等差数列。

1, 1, 2, 5, 7
 

数组 A 包含 N 个数,且索引从 0 开始。该数组子序列将划分为整数序列 (P0, P1, ..., Pk),P 与 Q 是整数且满足 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]

 

解答:

还是和上一题差不多的方法,但由于这题等差数列可以不相邻,比如1,2,3,5,6,7。可以选择1,3,5这样的子序列作为等差数列。那么考虑对于以i结尾的等差数列来说,对于k(0<k<i),若以k结尾的等差数列存在,且数量为X。则以i结尾的等差数列数量可以直接加X+1。但问题是以k结尾的等差数列可能有多种公差,每个公差有各自的数量。所以DP不仅要保存数量,还要保存公差。

那么我们使用一个vector< map <int ,int > >来作为dp数组。

含义:dp[i]是一个map,dp[i][j]表示以索引i结尾的、公差为j的等差数列有几个。

另外这题要用long,不然有的用例过不去。

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& A) {
        int n=A.size();
        if(n<3){return 0;}
        unordered_map<long,unordered_set<int> >mp;//mp[i]:stores all j which suits: A[j]==i
        for(int i=0;i<n;++i){
            mp[long(A[i])].insert(i);
        }
        int res=0;
        vector<unordered_map<long,long> >dp(n);//dp[i] is a map,dp[i][j]:i结尾、公差j的等差数列有几个
        for(int i=0;i<n-2;++i){
            for(int j=i+1;j<n-1;++j){
                long val=long(A[j])*2-long(A[i]);
                if(mp.count(val)){
                    for(int k:mp[val]){
                        if(k>j){
                            dp[k][val-A[j]]+=1;
                            // cout<<A[i]<<A[j]<<A[k]<<" "<<val<<endl;
                        }
                    }
                }
            }
        }
        long dif;
        for(int i=2;i<n;++i){
            for(int j=i-1;j>=2;--j){
                dif=long(A[i])-long(A[j]);//公差
                if(dp[j].count(dif)){
                    dp[i][dif]+=dp[j][dif];
                }
            }
        }
        for(int i=2;i<n;++i){
            for(auto& pair:dp[i]){
                res+=pair.second;
            }
        }
        return res;
    }
};

 

 

posted @ 2020-03-16 20:50  NeoZy  阅读(269)  评论(0编辑  收藏  举报