这是LeetCode周赛的一道题目,难度是中等。思路很简单,细节容易出错。
思路
只要不断比较斜率,出现新的斜率,线段数就加1。
细节1
class Solution {
public:
int minimumLines(vector<vector<int>>& pri) {
int n = pri.size();
sort (pri.begin(), pri.end());
if (n == 1) return 0;
double refer_k = (pri[1][1] - pri[0][1])*1.0 / (pri[1][0] - pri[0][0]);
int ans = 1;
for (int i = 2; i < n; ++i) {
double k = (pri[i][1] - pri[i-1][1])*1.0 / (pri[i][0] - pri[i-1][0]);
if (k == refer_k) continue;
refer_k = k;
ans++;
}
return ans;
}
};
这是我第一次提交的代码,使用了除法版本的斜率。显示通过了77个测试样例,死活不知道最后2个为什么不行?最后翻了讨论区,发现是浮点数的比较问题。
浮点数并非真正意义上的实数,在内存中存储的是近似数,所以用【==】去进行相等比较,出现的结果是很难预测的。解决办法有如下两个。
1.某种精度范围内的相等
可以设置精度为0.01, 如果fabs(a-b) <= 0.01,那么认为a和b是相等的。
2.转换成其他等式规避除法计算
我们可以将斜率的相等转换成乘法形式,这样就规避了除法产生的浮点数精度问题。
我们利用pair数据类型来记录斜率的分子和分母,用vector也行。先记录第一个线段的斜率,然后去求解后面线段的斜率。
class Solution {
public:
int minimumLines(vector<vector<int>>& pri) {
int n = pri.size();
sort (pri.begin(), pri.end(), [](const vector<int>& a, const vector<int>& b){return a[0] < b[0];});
if (n == 1) return 0;
pair<int, int> k = {pri[1][1] - pri[0][1], pri[1][0] - pri[0][0]};
int ans = 1;
for (int i = 2; i < n; ++i) {
pair<int, int> t = {pri[i][1] - pri[i-1][1], pri[i][0] - pri[i-1][0]};
if (k.first * t.second != k.second * t.first) {
k = t;
ans++;
}
}
return ans;
}
细节2
第二个细节是数据的表示范围。看到题目的要求,初步的想法是每个数都是在10的9次方以内,所以它们相减的数据也是在10的9次方以内。而int的数据范围是2*10的九次方以内,相减后的数据是不会超过int类型的。所以上述pair类型用的是int,但是提交后发现会报错。报错信息指出,两个int数相乘超过了int的表示范围,也就是下图if语句内部的相乘语句。
if (k.first * t.second != k.second * t.first)
两个int变量相乘,会构造一个新的临时变量int,然后将结果赋值给这个临时变量。两个比较大的int数值相乘导致了溢出。所以我们的pair类型不能是int。可以将两个pair类型都改为long long,也可以单独修改一处地方,因为只要表达式中存在一个long long 类型,临时变量的类型就是long long类型。
class Solution {
public:
int minimumLines(vector<vector<int>>& pri) {
int n = pri.size();
sort (pri.begin(), pri.end(), [](const vector<int>& a, const vector<int>& b){return a[0] < b[0];});
if (n == 1) return 0;
pair<long long, long long> k = {pri[1][1] - pri[0][1], pri[1][0] - pri[0][0]};
int ans = 1;
for (int i = 2; i < n; ++i) {
pair<int, int> t = {pri[i][1] - pri[i-1][1], pri[i][0] - pri[i-1][0]};
if (k.first * t.second != k.second * t.first) {
k = t;
ans++;
}
}
return ans;
}