1. 程式人生 > 其它 >【動態規劃】最長斐波那契數列和最長等差數列

【動態規劃】最長斐波那契數列和最長等差數列

今天總結一下Leetcode上的兩道看可以用同一解法實則不可生搬硬套的兩道相似題。

1、873. 最長的斐波那契子序列的長度 - 力扣(LeetCode) (leetcode-cn.com)

本題的難點在於狀態定義:與一般的dp題不同,這裡我們選擇將dp陣列定義為:dp[i][j]表示以arr[i],arr[j]結尾的斐波那契數列的最大長度。因為想要確定一個斐波那契數列需要三個值arr[i],arr[j],arr[k],如果直接定義首先會出現一個三重迴圈,並且許多細節難以言表。。。將狀態轉移陣列定義成上述表示可以根據性質:arr[k]+arr[i]=arr[j]來確定k的值,減小迴圈數。為了獲取下標需要一個hash表來將各個元素的下標提取出來(由於陣列嚴格遞增,所以我們不用擔心重複問題)。k由Index[arr[i]-arr[j]]唯一確定。

class Solution {
public:
    int lenLongestFibSubseq(vector<int>& arr) {
        int n=arr.size();
        unordered_map<int,int> Index;
        for(int i=0;i<n;i++){
            Index[arr[i]]=i;
        }
        int res=0;
        unordered_map<int,int> longest;
        for(int i=0;i<n;i++){
            for(int k=0;k<i;k++){
                if(Index.count(arr[i]-arr[k])&&arr[i]-arr[k]<arr[k]){
                    int j=Index[arr[i]-arr[k]];
                    longest[k*n+i]=longest[j*n+k]+1;
                    res=max(res,longest[k*n+i]+2);
                }
            }
        }
        return res>=3?res:0;
    }
};

時間複雜度:O(n2)

2、1027. 最長等差數列 - 力扣(LeetCode) (leetcode-cn.com)

這道題與上一道題十分相似,像我這樣的新手很容易慣性思維直接套用上題的解法。但其實在這裡是有問題的。因為題目中給定的陣列nums並沒有說明是嚴格遞增的,這意味著陣列中可能會出現重複元素,從而在確定第三維的下標時產生重複。因此在這裡我們將狀態定義為:dp[i][j]表示以nums[i]結尾的公差為j的等差數列的最大長度。根據題目所給的資料範圍:0<=nums[i]<=500我們可以確定公差的範圍-500<=d<=500,因此我們將公差加上500使之恆正,便於我們定義dp陣列。根據nums[i]-nums[j]+500來唯一確定公差。其他的便與LIS演算法相似。

class Solution {
public:
    int longestArithSeqLength(vector<int>& nums) {
        int n=nums.size();

        vector<vector<int>> dp(n,vector<int>(1001,0));
        int res=0;
        for(int i=0;i<n;i++){
            for(int j=0;j<i;j++){
                int d=nums[i]-nums[j]+500;
                dp[i][d]=max(dp[i][d],dp[j][d]+1);
                res=max(res,dp[i][d]);
            }
        }
        return res+1;
    }
};