文章目录
- [Acwing 2938. 周游世界【平面最远点】](https://www.acwing.com/solution/content/60906/)
- [Acwing 2142. 最小矩形覆盖【多单调指针】](https://www.acwing.com/problem/content/description/2144/)
- [HDU 2022(凸包中的最大三角形面积)](https://blog.csdn.net/qq_44135741/article/details/99089948)
Acwing 2938. 周游世界【平面最远点】
题意:求平面欧氏距离最远点
思路:单调找出所有对踵点,更新答案。
AC代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 5e4+10;
typedef long long LL;
struct point{
LL x,y;
bool operator < (point a){
if(x != a.x) return x < a.x;
return y < a.y;
}
bool operator == (point a) { return x == a.x && y == a.y ;}
}P[N];
point operator - (point a,point b) { return {a.x - b.x, a.y - b.y};}
LL cross(point a ,point b) { return a.x*b.y - a.y * b.x; }
LL area(point a,point b,point c) { return cross(b - a, c - a); }
LL get_dis(point a,point b) { return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); }
int sta[N],tp;
void andrew(int n)
{
sort(P,P+n);
n=unique(P,P+n)-P;
for(int i=0;i<n;i++)
{
while(tp >=2 && area(P[sta[tp-2]], P[sta[tp-1]], P[i]) <= 0) tp--;// ***
sta[tp++]=i;
}
int v=tp;
for(int i=n-2; i>=0 ;i--)
{
while(tp >v && area(P[sta[tp-2]], P[sta[tp-1]], P[i]) <= 0) tp--;
sta[tp++]=i;
}
tp--;
}
// *** 注意这里要防止出现多点一线的情况。容易证明多点一线的情况,最优解只存在于两端上,因此中间的点忽略即可。
LL rotate_calipers()
{
if(tp <= 2) return get_dis(P[sta[0]], P[sta[tp-1]]);
LL res=0;
for(int i=0, j=2; i<tp; i++)
{
auto & d = P[sta[i]], & e = P[sta[i + 1]];
while(area(d, e, P[sta[j]]) < area(d , e, P[sta[j + 1]])) j= (j + 1) % tp;
res=max(res, max(get_dis(d, P[sta[j]]),get_dis(e, P[sta[j]])));
}
return res;
}
int main()
{
int n; scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%lld %lld",&P[i].x,&P[i].y);
andrew(n);
printf("%lld",rotate_calipers());
system("pause");
return 0;
}
Acwing 2142. 最小矩形覆盖【多单调指针】
题意:求最小矩形覆盖
思路:维护三个单调指针
AC代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 5e4+10;
typedef long long LL;
const double eps = 1e-10;
const double pi = acos(-1);
int sign(double x){
if(fabs(x) < eps) return 0;
if(x < 0) return -1;
return 1;
}
int dcmp(double x, double y){
if(fabs(x - y) < eps) return 0;
if(x < y) return -1;
return 1;
}
struct point{
double x,y;
bool operator < (point a){
if(dcmp(x, a.x)!=0) return dcmp(x, a.x)==1 ? 0 : 1;
return y < a.y;
}
bool operator == (point a) { return dcmp(x, a.x) == 0 && dcmp(y, a.y) == 0;}
}P[N];
point operator - (point a,point b) { return {a.x - b.x, a.y - b.y};}
point operator + (point a,point b) { return {a.x + b.x, a.y + b.y};}
point operator * (point a,double b) { return {a.x * b, a.y * b}; }
point operator / (point a,double b) { return {a.x / b, a.y / b}; }
double cross(point a ,point b) { return a.x*b.y - a.y * b.x; }
double dot(point a,point b) { return a.x * b.x + a.y * b.y; }
double area(point a,point b,point c) { return cross(b - a, c - a) / 2; }
double get_dis(point a,point b) { return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); }
double length(point a) { return sqrt(a.x * a.x + a.y * a.y); }
double project(point a,point b, point c) { return dot(c - a, b - a)/ length(b - a); }
point unit(point a) { return a / length(a); }
point rotate(point a,double rad) { return {a.x * cos(rad) - a.y * sin(rad), a.x * sin(rad) + a.y * cos(rad)}; }
int sta[N],tp;
void andrew(int n)
{
sort(P,P+n);
n = unique(P,P+n) - P;
for(int i = 0; i < n; i++)
{
while(tp > 1 && sign(area(P[sta[tp-2]],P[sta[tp-1]],P[i])) != 1) tp--;// ***
sta[tp++]=i;
}
int v=tp;
for(int i = n-2 ;i>=0 ; i--)
{
while(tp > v && sign(area(P[sta[tp-2]], P[sta[tp-1]], P[i])) != 1) tp--;
sta[tp++]=i;
}
tp--;
}
// *** 注意这里要防止出现多点一线的情况。容易证明多点一线的情况,最优解只存在于两端上,因此中间的点忽略即可。
double ans=1e18;
point ans_point[4];
void rotate_calipers()
{
for(int i = 0, j =2, k = 2, l = 2; i < tp; i++)
{
while(area(P[sta[i]], P[sta[i + 1]], P[sta[j]]) < area(P[sta[i]], P[sta[i + 1]], P[sta[j + 1]])) j = (j + 1) % tp; // ***
while(project(P[sta[i]], P[sta[i + 1]], P[sta[k]]) < project(P[sta[i]], P[sta[i + 1]], P[sta[k + 1]])) k = (k + 1) % tp;
if(i==0) l = j;
while(project(P[sta[i]], P[sta[i + 1]], P[sta[l]]) > project(P[sta[i]], P[sta[i + 1]], P[sta[l + 1]])) l = (l + 1) % tp;
double w = project(P[sta[i]], P[sta[i + 1]], P[sta[k]]) - project(P[sta[i]],P[sta[i + 1]], P[sta[l]]);
double h = 2 * area(P[sta[i]], P[sta[i + 1]], P[sta[j]]) / length(P[sta[i + 1]] - P[sta[i]]);
// cout<<"# "<<i<<" "<<w<<" "<<h<<endl;
// cout<<"## "<<i<<" "<<j<<" "<<k<<" "<<l<<endl;
if(w * h < ans)
{
ans = w * h;
ans_point[0] = P[sta[i]] + unit(P[sta[i + 1]] - P[sta[i]]) * project(P[sta[i]], P[sta[i + 1]], P[sta[k]]);
ans_point[3] = P[sta[i]] + unit(P[sta[i + 1]] - P[sta[i]]) * project(P[sta[i]], P[sta[i + 1]], P[sta[l]]);
ans_point[1] = ans_point[0] + unit(rotate(P[sta[i + 1]]- P[sta[i]], pi / 2)) * h;
ans_point[2] = ans_point[3] + ans_point[1] - ans_point[0];
// cout<<i<<" "<<j<<" "<<k<<" "<<l<<endl;
// cout<<"# "<<w<<" "<< h <<endl;
// cout<<(P[sta[k]] - P[sta[l]]).x<<" "<<(P[sta[k]] - P[sta[l]]).y<<endl;
}
}
}
// *** 注意:这里不能用eps判断大小,直接单调性判断即可。因为fabs(ΔS)可能等于0小于eps,就会跳出循环,出现错误。而凸包本身就有单调性,就可以不用eps来判断。
int main()
{
int n; scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%lf %lf",&P[i].x,&P[i].y);
andrew(n);
// for(int i=0; i<=tp; i++) cout<<P[sta[i]].x<<" "<<P[sta[i]].y<<endl;
rotate_calipers();
printf("%.5f\n",ans);
int k=0;
for(int i=0; i < 4; i++)
if(dcmp(ans_point[k].y,ans_point[i].y) == 1 || dcmp(ans_point[k].y, ans_point[i].y) ==0 && dcmp(ans_point[k].x, ans_point[i].x) == 1) k = i;
for(int i=0; i<4; i++ , k = (k + 1) %4)
{
if(fabs(ans_point[k].x) < 5e-6) ans_point[k].x = 0;
if(fabs(ans_point[k].y) < 5e-6) ans_point[k].y = 0;
printf("%.5f %.5f\n",ans_point[k].x, ans_point[k].y);
}
return 0;
}
HDU 2022(凸包中的最大三角形面积)
题意:求凸包中的最大三角形面积
思路:看了题解,还是不知道为什么有单调性。
- 当前有i,j,k三指针,i向右移后,以i-j为底边,先让k单调跑(j先跑的话肯定跑不动)。找到最优点后,再以i-k为底边,让j单调跑。有个要注意的地方是:如果指针i与单调指针碰撞时,要后移单调指针避免碰撞,因为k的后移依赖于i-j为底边
#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=5e4 + 10;
const double eps = 1e-9;
int dcmp(double x, double y){
if(fabs(x - y) < eps) return 0;
if(x < y) return -1;
return 1;
}
int sign(double x){
if(fabs(x) < eps) return 0;
if(x < 0) return -1;
return 1;
}
struct point{
double x, y;
bool operator < (const point a) const {
if(dcmp(x, a.x)) return dcmp(x, a.x) < 0;
return dcmp(y, a.y) < 0;
}
bool operator == (const point a) const {
return !dcmp(x, a.x) && !dcmp(y, a.y);
}
};
point operator + (point a, point b) { return {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) { return {a.x - b.x, a.y - b.y}; }
point operator * (point a, double b) { return {a.x * b, a.y * b}; }
point operator / (point a, double b) { return {a.x / b, a.y / b}; }
double length(point a) { return sqrt(a.x * a.x + a.y * a.y); }
double cross(point a, point b) { return a.x * b.y - a.y * b.x; }
double dot(point a, point b) { return a.x * b.x + a.y * b.y; }
double area(point a, point b, point c) { return cross(b - a, c - a) / 2; }
double project(point a, point b, point c) { return dot(b - a, c - a) / length(b - a); }
point P[N];
int n, m;
int sta[N];
void andrew()
{
m = 0;
sort(P, P + n);
for(int i=0; i<n; i++)
{
while(m > 1 && sign(area(P[sta[m - 2]], P[sta[m - 1]], P[i])) <= 0) m--;
sta[m++] = i;
}
int v = m;
for(int i=n - 2; i>=0 ; i--)
{
while(m > v && sign(area(P[sta[m - 2]], P[sta[m - 1]], P[i])) <= 0) m--;
sta[m++] = i;
}
if(n > 1) m--;
}
double sol()
{
double res = 0;
for(int i=0, j = 1, k = 1; i<m; i++)
{
if(i == j) j++; //旋转卡壳要注意,有些特殊的题,如果指针i与单调指针碰撞时,要后移单调指针避免碰撞
//k的后移依赖于i-j为底边,要防止碰撞
while(area(P[sta[i]], P[sta[j]], P[sta[k]]) < area(P[sta[i]], P[sta[j]], P[sta[(k + 1) % m]])) k = (k + 1) % m;
while(area(P[sta[i]], P[sta[j]], P[sta[k]]) < area(P[sta[i]], P[sta[(j + 1) % m]], P[sta[k]])) j = (j + 1) % m;
// cout<<"# "<<i<<" "<<j<<" "<<k<<endl;
res = max(res, area(P[sta[i]], P[sta[j]], P[sta[k]]));
}
return res;
}
int main()
{
while(scanf("%d", &n) != EOF)
{
for(int i=0 ;i<n ;i++) scanf("%lf %lf", &P[i].x, &P[i].y);
andrew();
printf("%.2f\n", sol());
}
system("pause");
return 0;
}