思路:
题目的主要信息:
- 数轴上有n个点,每个点有自己的坐标和数值
- 从任意点出发,求经过K个不同数值点的最小花费
方法一:暴力法(超时)
具体做法:
对排序后的坐标点,每个点都可以作为一个区间的起点,我们遍历所有起点,往后遍历,找到后续区间是否有k个不同数值的点(使用哈希表,哈希表key值记录坐标上的数值,哈希表大小达到k即可),对于所有的区间,我们找到一个最小值即可。
class Solution { public: static bool comp(vector<int>& a, vector<int>& b){ //重载比较 return a[1] < b[1]; } int solve(int n, int k, vector<vector<int> >& card) { sort(card.begin(), card.end(), comp); //按照坐标排序 int res = INT_MAX; for(int i = 0; i < n; i++){ //每个点都可以作为区间起点 unordered_map<int, int> mp; mp.clear(); for(int j = i; j < n; j++){ //往后找k个不同的数值 mp[card[j][0]]++; //记录不同的数值 if(mp.size() == k){ //找到k个 res = min(res, card[j][1] - card[i][1]); //维护最小值 break; } } } if(res == INT_MAX) //无法经过k个不同的点 return -1; return res; } };
复杂度分析:
- 时间复杂度:,两层循环,这是无序哈希表,可以直接访问删除,复杂度为,排序的复杂度属于低次幂,舍去
- 空间复杂度:,哈希表最大长度为
方法二:排序+滑动窗口+哈希表
具体做法:
对所有点按照坐标排序,然后从坐标最前出发,使用哈希表维护不同的数值及这个数值的点的数量,维持一个窗口,当哈希表长度等于k时,窗口左端右移,并从哈希表中减少相应左边窗口值得数量,当数量为0时,从哈希表中删去这个值直到哈希表长度小于k。而窗口右端循环一次自然增长。
class Solution { public: static bool comp(vector<int>& a, vector<int>& b){ //重载比较 return a[1] < b[1]; } int solve(int n, int k, vector<vector<int> >& card) { sort(card.begin(), card.end(), comp); //按照坐标排序 int res = INT_MAX; unordered_map<int, int> mp; //统计不同的数值 for(int l = 0, r = 0; r < n; r++){ //维持窗口 mp[card[r][0]]++; while(l < r && mp.size() == k){ //缩小区间 res = min(res, card[r][1] - card[l][1]); //记录最小值 mp[card[l][0]]--; if(mp[card[l][0]] == 0) mp.erase(card[l][0]); l++; } } if(res == INT_MAX) //无法经过k个不同的点 return -1; return res; } };
复杂度分析:
- 时间复杂度:,窗口左端最多增长到,因此内循环总共会有次,因此复杂度相当于,而与排序的比起来是低次幂,故舍去
- 空间复杂度:,哈希表长度最大为