算法导论33(计算几何学)

线段的性质
(1)叉积
p1×p2=x1*y2-x2*y1=-p2×p1

(2)确定连续线段是向左转还是向右转
p0p1、p1p2
(p1-p0)×(p2-p0)>0,向左转;
(p1-p0)×(p2-p0)<0,向右转;
(p1-p0)×(p2-p0)=0,p0、p1、p2三点共线。

(3)判断两条线段是否相交

struct point
{
    int x,y;
};

int direction(point pi,point pj,point pk)
{
    //(pk-pi)×(pj-pi)
    return (pk.x-pi.x)*(pj.y-pi.y)-(pj.x-pi.x)*(pk.y-pi.y);
}

bool onSegment(point pi,point pj,point pk)
{
    if(min(pi.x,pj.x)<=pk.x&&pk.x<=max(pi.x,pj.x)&&min(pi.y,pj.y)<=pk.y&&pk.y<=max(pi.y,pj.y))return true;
    else return false;
}

bool segmentsIntersect(point p1,point p2,point p3,point p4)
{
    int d1=direction(p3,p4,p1);
    int d2=direction(p3,p4,p2);
    int d3=direction(p1,p2,p3);
    int d4=direction(p1,p2,p4);
    if((d1>0&&d2<0)||(d1<0&&d2>0)||(d3>0&&d4<0)||(d3<0&&d4>0))return true;
    else if(d1==0&&onSegment(p3,p4,p1))return true;
    else if(d2==0&&onSegment(p3,p4,p2))return true;
    else if(d3==0&&onSegment(p1,p2,p3))return true;
    else if(d4==0&&onSegment(p1,p2,p4))return true;
    else return false;
}

确定任意一对线段是否相交

寻找凸包
Graham扫描法,时间复杂度为O(nlogn)

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

struct point
{
    int x,y;
}p;

int crossProduct(point a,point b,point c)
{
    return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}

//距离的平方
int dist(point a,point b)
{
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}

bool cmp(point b,point c)
{
    if(crossProduct(p,b,c)>0||(crossProduct(p,b,c)==0&&dist(p,b)<dist(p,c)))return true;
    return false;
}

vector<point> GrahamScan(vector<point> Q)
{
    int bottom=0;
    for(int i=1;i<Q.size();++i)
    {
        if((Q[i].y<Q[bottom].y)||(Q[i].y==Q[bottom].y&&Q[i].x<Q[bottom].x))bottom=i;
    }
    p=Q[bottom];
    Q[bottom]=Q[0];
    Q[0]=p;
    sort(Q.begin()+1,Q.end(),cmp);
    for(vector<point>::iterator i=Q.begin()+1;i+1!=Q.end();)
    {
        if(crossProduct(p,*i,*(i+1))==0)i=Q.erase(i);
        else ++i;
    }
    vector<point> S;
    if(Q.size()<3)return S;
    S.push_back(Q[0]);
    S.push_back(Q[1]);
    S.push_back(Q[2]);
    for(int i=3;i<Q.size();++i)
    {
        while(crossProduct(*(S.rbegin()+1),*S.rbegin(),Q[i])<=0)S.pop_back();
        S.push_back(Q[i]);
    }
    return S;
}

//输入
//9
//1 1
//1 2
//1 3
//2 1
//2 2
//2 3
//3 1
//3 2
//3 3
//输出
//(1,1) (3,1) (3,3) (1,3)

int main()
{
    int n;
    cin>>n;
    vector<point> Q(n);
    for(int i=0;i<n;++i)cin>>Q[i].x>>Q[i].y;
    vector<point> S=GrahamScan(Q);
    for(int i=0;i<S.size();++i)cout<<'('<<S[i].x<<','<<S[i].y<<')'<<' ';
    return 0;
}

寻找最远点对
凸包+旋转卡壳,时间复杂度为O(nlogn)+O(n)=O(nlogn)

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

struct point
{
    int x,y;
}p;

int crossProduct(point a,point b,point c)
{
    return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}

//距离的平方
int dist(point a,point b)
{
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}

bool cmp(point b,point c)
{
    if(crossProduct(p,b,c)>0||(crossProduct(p,b,c)==0&&dist(p,b)<dist(p,c)))return true;
    return false;
}

vector<point> GrahamScan(vector<point> Q)
{
    int bottom=0;
    for(int i=1;i<Q.size();++i)
    {
        if((Q[i].y<Q[bottom].y)||(Q[i].y==Q[bottom].y&&Q[i].x<Q[bottom].x))bottom=i;
    }
    p=Q[bottom];
    Q[bottom]=Q[0];
    Q[0]=p;
    sort(Q.begin()+1,Q.end(),cmp);
    for(vector<point>::iterator i=Q.begin()+1;i+1!=Q.end();)
    {
        if(crossProduct(p,*i,*(i+1))==0)i=Q.erase(i);
        else ++i;
    }
    vector<point> S;
    if(Q.size()<3)return S;
    S.push_back(Q[0]);
    S.push_back(Q[1]);
    S.push_back(Q[2]);
    for(int i=3;i<Q.size();++i)
    {
        while(crossProduct(*(S.rbegin()+1),*S.rbegin(),Q[i])<=0)S.pop_back();
        S.push_back(Q[i]);
    }
    return S;
}

//旋转卡壳算法
int rotatingCalipers(vector<point> S)
{
    int n=S.size(),j=1,maxDist=0;
    S.push_back(S[0]);
    for(int i=0;i<n;++i)
    {
        while(crossProduct(S[i],S[i+1],S[j+1])>crossProduct(S[i],S[i+1],S[j]))j=(j+1)%n;
        maxDist=max(maxDist,max(dist(S[i],S[j]),dist(S[i+1],S[j+1])));
    }
    return maxDist;
}

//输入
//9
//1 1
//1 2
//1 3
//2 1
//2 2
//2 3
//3 1
//3 2
//3 3
//输出
//8

int main()
{
    int n;
    cin>>n;
    vector<point> Q(n);
    for(int i=0;i<n;++i)cin>>Q[i].x>>Q[i].y;
    vector<point> S=GrahamScan(Q);
    cout<<rotatingCalipers(S)<<endl;
    return 0;
}

寻找最近点对
分治,时间复杂度为O(nlogn)

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N=100;

struct point
{
    int x,y;
}p[N];

double dist(point a,point b)
{
    return sqrt(double((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)));
}

bool cmpx(point a,point b)
{
    return a.x<b.x;
}

bool cmpy(point a,point b)
{
    return a.y<b.y;
}

double minDist(int l,int r)
{
    if(l+1==r)return dist(p[l],p[r]);
    if(l+2==r)return min(min(dist(p[l],p[l+1]),dist(p[l+1],p[r])),dist(p[l],p[r]));
    int mid=l+(r-l)/2;
    double delta=min(minDist(l,mid),minDist(mid+1,r));
    vector<point> q;
    for(int i=l;i<=r;++i)
    {
        if(abs(p[i].x-p[mid].x)<=delta)q.push_back(p[i]);
    }
    sort(q.begin(),q.end(),cmpy);
    for(int i=0;i<q.size();++i)
    {
        for(int j=1;j<=7;++j)
        {
            if(i+j<q.size())
            {
                if(dist(p[i],p[i+j])<delta)delta=dist(p[i],p[i+j]);
                else break;
            }
        }
    }
    return delta;
}

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;++i)cin>>p[i].x>>p[i].y;
    sort(p,p+n,cmpx);
    cout<<minDist(0,n-1)<<endl;
    return 0;
}

你可能感兴趣的:(算法导论33(计算几何学))