P1652 圆
大意:
给出 n 个圆,保证任意两个圆都不相交。
然后给出两个点
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
(x1,y1),(x2,y2)
(x1,y1),(x2,y2),保证均不在某个圆上,要从
(
x
1
,
y
1
)
→
(
x
2
,
y
2
)
(x1,y1)→(x2,y2)
(x1,y1)→(x2,y2)画条曲线,问这条曲线最少穿过多少次圆的边界?
思路:
因为是画曲线,而且任意两个圆都不相交,所以只要两个点不是分别在不同的圆里面,都有办法找到一条曲线绕过所有的圆,所以只需要判断,对于每个圆,是否两个点分别属于圆内和圆外即可
#include<bits/stdc++.h>
using namespace std;
double dis(int x1,int y1,int x2,int y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main(){
int x1,y1,x2,y2,ans=0;
int y[105][3],n;
cin>>n;
for(int i=0;i<3;i++)
for(int j=0;j<n;j++)
cin>>y[j][i];
cin>>x1>>y1>>x2>>y2;
for(int i=0;i<n;i++)
if(dis(x1,y1,y[i][0],y[i][1])<y[i][2]!=dis(x2,y2,y[i][0],y[i][1])<y[i][2]) ans++;
cout<<ans<<endl;
return 0;
}
P2181 对角线
大意:
对于一个 nnn 个顶点的凸多边形,它的任何三条对角线都不会交于一点。请求出图形中对角线交点的个数。
思路:
找规律题…
#include<bits/stdc++.h>
long long a1[100000];
long long a2[100000];
long long a3[100000];
int main()
{
long long n;
scanf("%lld",&n);
int t=0;
for(int i=1;i<=n;i++)
{
a1[i]=a1[i-1]+i;
}
for(int i=1;i<=n;i++)
{
a2[i]=a2[i-1]+a1[i];
}
for(int i=1;i<=n;i++)
{
a3[i]=a3[i-1]+a2[i];
}
printf("%lld",a3[n-3]);
return 0;
}
P1257 平面上的最接近点对
大意:
给定平面上 nnn 个点,找出其中的一对点的距离,使得在这 nnn 个点的所有点对中,该距离为所有点对中最小的。
思路:
裸题,直接分治求
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
int n;
const double INF = 0x3f3f3f3f;
struct node
{
double x, y;
}a[N];
bool cmpx(node a,node b){
return a.x < b.x;
}
bool cmpy(node a,node b){
return a.y < b.y;
}
double dis(node a,node b){
return (sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)));
}
double solve(int l,int r){
if(l==r){
return INF; //当前区间只有一个点,返回INF,避免更新答案
}
int mid = l + r >> 1;
double midnum = a[mid].x; //区间中点
double res = min(solve(l, mid), solve(mid + 1, r)); //分治求两边区间的最小距离
sort(a + l, a + r + 1, cmpy); //按照y轴排序,这样后面在找的时候只需要找最近的几个点即可
int temp[N], cnt = 0;
for (int i = l; i <= r; i++){
if(a[i].x>=midnum-res&&a[i].x<=midnum+res){
temp[cnt++] = i; //将“中间地带”的点加入temp,准备判断
}
}
for (int i = 0; i < cnt;i++){
for (int j = i - 1; j >= 0 && (a[temp[i]].y - a[temp[j]].y < res) ;j--) //因为已经排好序了,所以只会遍历到最近的几个点,可以证明最多只有6个点,所以复杂度为O(n)
{
res = min(res, dis(a[temp[i]], a[temp[j]]));
}
}
return res;
}
int main(){
cin>>n;
for (int i = 0; i < n;i++){
cin >> a[i].x >> a[i].y;
}
sort(a, a + n, cmpx);
printf("%.4lf\n",solve(0, n - 1));
return 0;
}
P1142 轰炸
大意:
给出n个点,问一条直线最多可以穿过多少个点 ( 1 ≤ n ≤ 700 ) (1\leq n \leq 700) (1≤n≤700)
思路:
n很小,直接暴力一波就行,先选出两个点,确定直线,然后遍历一遍看有多少点在这个直线上(这里因为用了一堆没啥用的结构体,需要开一下O2优化,不过无所谓了,领悟思路即可~)
#include<bits/stdc++.h>
using namespace std;
const int N = 700 + 5;
typedef long long LL;
int n;
const double eps = 1e-6;
// 精度三态函数(>0, <0, =0)
int dcmp(double x) {
if (fabs(x) < eps)
return 0; // 如果差值非常小,那么返回0
else if (x < 0)
return -1;
else
return 1;
}
// 定义向量
struct Point {
double x, y;
Point() {}
Point(double _x, double _y) : x(_x), y(_y) {}
// 向量加减
Point operator+(const Point a) const { return Point(x + a.x, y + a.y); }
Point operator-(const Point a) const { return Point(x - a.x, y - a.y); }
// 向量比较:等于、小于
bool operator==(const Point a) const {
return dcmp(x - a.x) == 0 && dcmp(y - a.y) == 0;
}
bool operator<(const Point a) const {
if (x != a.x) // 先比较x,然后比较y
return x < a.x;
else
return y < a.y;
}
// 叉乘、点乘
double operator*(const Point a) const {
return x * a.y - y * a.x;
} // 叉乘(外积)
double operator^(const Point a) const {
return x * a.x + y * a.y;
} // 点乘(内积)
};
typedef Point Vector;
Vector a[N];
int main(){
cin >> n;
for (int i = 0; i < n;i++){
cin >> a[i].x >> a[i].y;
}
int res = 0;
for (int i = 0; i < n - 1;i++){
for (int k = i+1; k < n;k++){
int cnt = 2;
for (int j = 0; j < n; j++){
if(i==j||k==j)
continue;
Point v1 = a[i] - a[k], v2 = a[k] - a[j];
if (v1*v2 == 0){
cnt++;
}
}
res = max(res, cnt);
}
}
cout << res << endl;
return 0;
}
P7042 「MCOI-03」正方
大意:
给出abcd四个数,要求在一个正方形内选一个点,向四个顶点连线,分成的三个三角形面积比为 a : b : c : d a:b:c:d a:b:c:d,问有多少种方法
思路:
感觉这完全不是计算几何题啊…
可以发现只可能有 0 1 4 8的情况,因为分成四个三角形,所以将abcd从小到大排序的话,必须满足a+d=b+c,所以只要不符合的都是0,a=b=c=d的都是1,如果a=b则是4,其他情况都是8
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef unsigned long long ULL;
ULL n, a[5];
int main(){
cin >> n;
while(n--){
for (int i = 0; i < 4;i++){
cin >> a[i];
}
sort(a, a + 4);
if (a[0]+a[3]!=a[1]+a[2]){
cout << 0 << endl;
}
else if(a[0]==a[3]){
cout << 1 << endl;
}
else if(a[0]==a[1]){
cout << 4 << endl;
}
else{
cout << 8 << endl;
}
}
return 0;
}
P2735 [USACO3.4]网 Electric Fences
大意:
问 ( 0 , 0 ) , ( n , m ) ( p , 0 ) (0,0),(n,m)(p,0) (0,0),(n,m)(p,0)组成的三角形,内部的点格点有多少
思路:
1定理:pick定理 pick定理是指一个计算点阵中顶点在格点上的多边形面积公式,该公式可以表示为2S=2a+b-2,其中a表示多边形内部的点数,b表示多边形边界上的点数,s表示多边形的面积。
2定理:(0,0)到(n,m)的线段上点的个数=gcd(n,m)+1, 定理2就是扩展欧几里得的特殊情况
ps.逆用pick定理时一定不能用(2s-b+2)/2,应该用s-b/2+1
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
LL n, m,p,res,s,b;
int main(){
cin>>n>>m>>p;
s=(p*m)/2;
b = __gcd(n, m) + __gcd(abs(n - p), m) + p;
cout << s - b / 2 + 1 << endl;
return 0;
}