1. 解析
题目大意,求解最长的子序列,要求改子序列中的元素两两之间能整除。一道DP题
2. 分析
只要涉及DP的题目都不简单,我最初想到的是用DFS去求解,结果无法OJ。参考了@Grandyang的解法,其实本题的难点就在于子序列动态增长的过程中怎么记录当前最长子序列的起始索引,本思路采用一个比较巧妙的结构,利用pair键值对存储,dp[i]表示序列nums[0, i]范围内最长的子序列和对应的起始索引,之所以将起始的索引保存下来,是为了更好地回溯查找到整个最长子序列的路径,类似于弗洛伊德算法求解最短路径问题。
例如,[1, 2, 4, 6, 8]
i = 0,dp[0] = (1, 0) —— 1 mx_index = 0
i = 1, dp[1] = (2, 0) —— 1, 2 mx_index = 1
i = 2, dp[2] = (3, 1) —— 1, 2, 4 mx_index = 2
i = 3, dp[3] = (3, 1) —— 1, 2, 6 mx_index = 3
i = 4, dp[4] = (4, 2) —— 1, 2, 4, 8 mx_index = 4
从上面可以看到,根据mx_index就可以回溯到最长子序列中的各个具体的元素.
class Solution {
public:
vector<int> largestDivisibleSubset(vector<int>& nums){
sort(nums.begin(), nums.end());
vector<pair<int, int>> dp(nums.size()); //键值对key---当前子序列的最大长度,value---上一个起始的位置
int mx = 0, mx_index = 0;
vector<int> res;
for (int i = 0; i < nums.size(); ++i){
for (int j = i; j >= 0; --j){
if (nums[i] % nums[j] == 0 && dp[i].first < dp[j].first + 1){
dp[i].first = dp[j].first + 1;
dp[i].second = j; //记录上一个最优的位置
if (mx < dp[i].first){ //更新最优值
mx = dp[i].first;
mx_index = i;
}
}
}
}
for (int i = 0; i < mx; ++i){
res.push_back(nums[mx_index]); //取出当前元素
mx_index = dp[mx_index].second; //回溯上一个元素的位置
}
return res;
}
};