考点:数学加暴力枚举
由于直线可以被表示成y = kx + b
,因此我们可以枚举两个点求出斜率和截距。斜率和截距都确定了,这条直线就确定了。
由于暴力枚举有可能枚举到同一条直线,因此要去重。如果K和B都相等,证明两条直线一样,就不加了。
重点是学习枚举的方法,有两个方法:
由于要枚举两个点,可以直接枚举x1,x2,y1,y2
也可以先把所有点存下来,然后对同一段线性空间套两个for循环。
#include <iostream>
#include <map>
#include <functional>
#include <vector>
using namespace std;
map<pair<double, double>, int> m;//存斜率和截距
vector<pair<double, double>> v;//总共有20 * 19个点,开大一点点,用vector也可以
int main()
{
//题目给的是坐标
//这两个循环是用来把所有的点都放进vector里面
//其实就是让坐标相互组合
for(double i = 0; i <= 19; i++)
for(double j = 0; j <= 20; j++)
v.push_back({i, j});
int ans = 21 + 20;//21条横线+20条竖线
//vector里面已经是点了
//这个时候套两层循环可以让两个点互相组合
for(int i = 0; i < v.size(); i++)
for(int j = 0; j < v.size(); j++)
{
if(v[i].first == v[j].first || v[i].second == v[j].second) continue;//横坐标相同或者纵坐标相同
double k = (v[j].second - v[i].second) / (v[j].first - v[i].first);
double b = (v[j].first * v[i].second - v[i].first * v[j].second) / (v[j].first - v[i].first);
//求b的时候不能写成注释的样子,由于k是刚算出来的值
//继续拿他做运算很容易损失精度,要把k化简掉
//double b = (v[j].second - k * v[j].first);
if(m[{k, b}] == 0) m[{k, b}] = 1, ans++;
}
cout << ans;
}
总结一下什么时候可以用两层循环枚举,就是当你想要让数据里的元素彼此互相组合的时候就大胆使用把。
还可以这么写
for(int x1 = 0; x1 < n; x1++)
for(int y1 = 0; y1 < m; y1++)
for(int x2 = 0; x2 < n; x2++)
for(int y2 = 0; y2 < m; y2++)
{
//里面逻辑一样
}
和上面道理是一样的,相当于上面把两层循环拆开写而已。时间复杂度也没有变。
两种枚举方式都可以学习一下!!!。
答案是40257