2023牛客多校第一场B Anticomplementary Triangle

文章讨论了如何在给定的点集中找到面积最大的三角形,提出了一种基于双指针的O(n)时间复杂度算法。通过证明在特定条件下,三角形面积关于坐标是单峰函数,优化了遍历方式,减少了复杂度,实现了更高效的求解方法。

①:有结论:面积最大的三角形即为所求
证明:若有点在面积最大的三角形对应“AnticomplementaryTriangle”“Anticomplementary Triangle”AnticomplementaryTriangle之外,一定能取得更大的面积。如下图:

②:O(n)O(n)O(n)时间内找到这个三角形
1.O(n2)1.O(n^2)1.O(n2)显然,因为i,ji,ji,j固定后,面积关于坐标k的函数一定是单峰函数,j,kj,kj,k可以当双指针做,复杂度少一个nnn
2.2.2.假如依然按双指针做,固定i,j0i,j0i,j0时找到最优的k0k0k0,固定i,j1i,j1i,j1时找到最优的k1k1k1,固定i,j2i,j2i,j2时找到最优的k2k2k2,并且固定i,k0i,k0i,k0时找到的最优解是j2j2j2
现证明S(i,j1,k1)<S(i,j2,k2)S(i,j1,k1)<S(i,j2,k2)S(i,j1,k1)<S(i,j2,k2)
根据定义可列出如下不等式:
S(i,j1,k0)<S(i,j2,k0)<S(i,j2,k1)<S(i,j2,k2)S(i,j1,k0)<S(i,j2,k0)<S(i,j2,k1)<S(i,j2,k2)S(i,j1,k0)<S(i,j2,k0)<S(i,j2,k1)<S(i,j2,k2)
满足S(i,j1,k0)<S(i,j2,k0)S(i,j1,k0)<S(i,j2,k0)S(i,j1,k0)<S(i,j2,k0) 的情况下,图形如下所示,故可得到另一个不等式:S(i,j1,k1)<S(i,j2,k1)S(i,j1,k1)<S(i,j2,k1)S(i,j1,k1)<S(i,j2,k1)
得证
请添加图片描述
原本是jjj依次枚举,kkk双指针跳转,这个结论可得到j,kj,kj,k绑定,iiij,kj,kj,k的结合体做双指针,复杂度O(n)O(n)O(n)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+3;
struct point{
	int x,y;
}p[N];
#define gc getchar
inline int rd(){
	int x=0,fl=1;char ch=gc();
	for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
	for (;48<=ch&&ch<=57;ch=gc())x=x*10+(ch^48);
	return x*fl;
}
int cross(point p0,point p1,point p2){
	return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
int li,lj,lk,n,i;
#define upd if(tmp>ans)ans=tmp,li=i,lj=j,lk=k;
void rotate(){
	int ans=0;
	p[n]=p[0];
	for (int i=0,j=1,k=2;i<n;i++){
		while (1){
			int tmp,jj=j,kk=k;
			while ((tmp=cross(p[i],p[j],p[k]))<cross(p[i],p[j],p[k+1])) k=(k==n-1?0:k+1);
			upd
			while ((tmp=cross(p[i],p[j],p[k]))<cross(p[i],p[j+1],p[k])) j=(j==n-1?0:j+1);
			upd
			if (j==jj && k==kk) break;
		}
	}
}
signed main(){
	n=rd();
	for (i=0;i<n;i++) p[i].x=rd(),p[i].y=rd();
	rotate();
	printf("%lld %lld %lld",li+1,lj+1,lk+1);
}

其实还可以类似jjj的证法证一证iii,三个数一起循环看起来也更优美一点,代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+3;
struct point{
    int x,y;
}p[N];
#define gc getchar
inline int rd(){
	int x=0,fl=1;char ch=gc();
	for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
	for (;48<=ch&&ch<=57;ch=gc())x=x*10+(ch^48);
	return x*fl;
}
int cross(point p0,point p1,point p2){
    return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
int li,lj,lk,n,i;
#define upd if(tmp>ans)ans=tmp,li=i,lj=j,lk=k;
void rotate(){
    int ans=0;
    p[n]=p[0];
    int i=0,j=1,k=2;
    bool fl=0;
    while (k && !fl){
    	int ii=i,jj=j,kk=k,tmp;
        while((tmp=cross(p[i],p[j],p[k]))<cross(p[i],p[j],p[k+1]))
        	if (k==n-1) fl=1,k=0;
        	else k++;
		upd
        while((tmp=cross(p[i],p[j],p[k]))<cross(p[i],p[j+1],p[k])) j=(j==n-1?0:j+1);
		upd
        while((tmp=cross(p[i],p[j],p[k]))<cross(p[i+1],p[j],p[k])) i=(i==n-1?0:i+1);
        upd
        if (i==ii && j==jj && k==kk) k=(k==n-1?0:k+1);
    }
}
signed main(){
	n=rd();
	for (i=0;i<n;i++) p[i].x=rd(),p[i].y=rd();
	rotate();
	printf("%lld %lld %lld",li+1,lj+1,lk+1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值