「2017ACM/ICPC亚洲区域赛北京站现场赛G」Liaoning Ship's Voyage【计算几何】

Liaoning Ship’s Voyage

L i a o n i n g   s h i p Liaoning\ ship Liaoning ship, which named after a province of China, is the first aircraft carrier commissioned into the P e o p l e ′ s   L i b e r a t i o n   A r m y   N a v y . People's\ Liberation\ Army\ Navy. Peoples Liberation Army Navy. It was bought from U k r a i n e Ukraine Ukraine as a stripped hulk and was rebuilt by China as an important part of China’s blue water Navy plan. Liaoning ship has sailed far into the Pacific Ocean for serval times, which shows the power and resolve of China to defend her integrity of territory.

Now L i a o n i n g   s h i p Liaoning\ ship Liaoning ship is on a new voyage to the Atlantic Ocean for a maneuver! The vast maneuver region on the ocean can be seen as an n × n n\times n n×n grid which has n × n n\times n n×n crosspoints. Each crosspoint stands for a check point of the maneuver region. Liaoning starts from the bottom-left check point whose coordinate is ( 0 , 0 ) (0,0) (0,0), and its destination is the upper-right checkpoint whose coordinate is ( n − 1 , n − 1 ) . (n-1,n-1). (n1,n1). The positive side of the x x x axis points to the right, and the positive side of the y axis points up. All check points’ coordinates are integral. During each move, L i a o n i n g Liaoning Liaoning can go from one check point to its adjacent 8 8 8 check points along a straight line, and each move takes L i a o n i n g Liaoning Liaoning one day. Some check points are not available to go due to the bad weather. And, as you know, on the Atlantic Ocean, there is a Bermuda Triangle in which many ships and planes were missing. L i a o n i n g Liaoning Liaoning can’t take risk to go into that triangle. Of course, Liaoning can’t go outside the maneuver region. Please figure out a route for Liaoning to reach its destination as soon as possible.

Input

There are no more than 30 30 30 test cases.

For each case:

The first line is an integer n ( 2 ≤ n ≤ 20 ) , n( 2 \leq n \leq 20), n(2n20), meaning that the maneuver region is an n × n n\times n n×n grid.

The seconds line contains six float numbers x 1 , y 1 , x 2 , y 2 , x 3 , y 3 ( − 100 ≤ x 1 , y 1 , x 2 , y 2 , x 3 , y 3 ≤ 100 ) x_1,y_1,x_2,y_2,x_3,y_3( -100 \leq x_1,y_1,x_2,y_2,x_3,y_3 \leq 100) x1,y1,x2,y2,x3,y3(100x1,y1,x2,y2,x3,y3100) which have at most 2 2 2 digits after the decimal point, indicating the coordinates of the vertices of Bermuda Triangle are ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1), (x_2,y_2) (x1,y1),(x2,y2) and ( x 3 , y 3 ) . (x_3,y_3). (x3,y3). Liaoning ship can’t go into that triangle, but going along its edges or touching its vertices are allowed.

Then an n×n character matrix consists of ‘.’ and ‘#’ follows, indicating the weather condition of all check points. ‘.’ Means good weather and ‘#’ means bad weather. The bottom-left character stands for the weather of check point ( 0 , 0 ) , (0,0), (0,0), the character on the right side of it stands for ( 1 , 0 ) , (1,0), (1,0), and the upper-right character stands for ( n − 1 , n − 1 ) . (n-1,n-1). (n1,n1).

It is guaranteed that check point ( 0 , 0 ) (0,0) (0,0) is not inside the Bermuda Triangle or on the edges of the triangle.

Output

For each test case, print the minimum days Liaoning ship needs to reach its destination. If it is impossible for Liaoning to reach its destination, print -1 instead.

Sample Input

3
0.5 1.5 1.5 1.5 1 0.5
.#.
...
..#
3
0.5 1.5 1.5 1.5 1 0.5
.#.
..#
..#

Sample Output

3
-1

题意

  • 就是说有一个地图,你需要从 ( 0 , 0 ) (0,0) (0,0)走到 ( n − 1 , n − 1 ) (n-1,n-1) (n1,n1),每次你可以向相邻的八个整点沿直线走,时间花费都是 1 1 1,不能走到’#'中,而且走的路径不能到达给定的一个三角形中,但是可以沿着三角形的一条边走或者经过一个顶点,然后求最小的从 ( 0 , 0 ) (0,0) (0,0)走到 ( n − 1 , n − 1 ) (n-1,n-1) (n1,n1)的时间

题解

  • n n n很小,考虑暴力找出所有可行路径然后 b f s bfs bfs
  • 重点以及难点就是判断一条路径 s → e s\rightarrow e se是否与 △ A B C \triangle ABC ABC在内部有公共点,步骤如下
    • 首先如果两个点 s s s e e e都在 △ A B C \triangle ABC ABC内部的话(可以根据三个小三角形面积之和等于大三角形面积),进一步判断一下两个点是否都在三角形的某一条边上,如果是,则合法,否则不合法
    • 现在主要考虑枚举三角形的每一条边与 s → e s\rightarrow e se求交,如果没有交点或者平行或者重合,继续枚举,否则剩下的情况就是不平行而且相交的情况,求出交点,如果交点 i n t e r s e c t intersect intersect不等于任何一个线段端点,这时可以直接判定在三角形内部有公共点,那么显然不合法,否则的话如果交点等于三角形的一个顶点,那么也就是下图这种情况
      在这里插入图片描述
      在这里插入图片描述
      直接判断 ∠ C B A \angle CBA CBA ∠ C B e \angle CBe CBe大小即可判断是否相交

代码

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-8;
const int maxn=50;
#define pi acos(-1.0)
int sgn(double k) {return k<-eps?-1:(k<eps?0:1);}
double Acos(double k) {return k>=1?0:(k<=-1?pi:acos(k));}
double Sqrt(double k) {return sgn(k)<=0?0:sqrt(k);}
char s[maxn][maxn];
bool vis[maxn][maxn];
bool ok[25][25][25][25];
int n,dir[8][2]={{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0}};

struct point{
    double x,y;
    point(double a=0,double b=0) {x=a;y=b;}
    point operator+(point other) {return point(x+other.x,y+other.y);}
    point operator-(point other) {return point(x-other.x,y-other.y);}
    point operator*(double k) {return point(x*k,y*k);}
    friend bool operator==(const point &p1,const point &p2) {return sgn(p1.x-p2.x)==0&&sgn(p1.y-p2.y)==0;}
    double operator*(point other) {return x*other.x+y*other.y;}
    double operator^(point other) {return x*other.y-y*other.x;}
    friend double area(point a,point b,point c) {return fabs((b-a)^(c-a))/2;}
    friend int point_to_line(point p,point s,point e) {return -sgn((e-s)^(p-s));}
    friend double len(point p) {return Sqrt(p.x*p.x+p.y*p.y);}
    friend double angle(point p1,point p2) {
        if(point_to_line(p2,point(0,0),p1)<=0) return Acos((p1*p2)/len(p1)/len(p2));
        return 2*pi-Acos((p1*p2)/len(p1)/len(p2));
    }
};

struct line{
    point s,e;
    line () {}
    line (point a,point b) {s=a;e=b;}
    friend point intersect(line l1,line l2) {
        double k=((l2.e-l2.s)^(l2.s-l1.s))/((l2.e-l2.s)^(l1.e-l1.s));
        return l1.s+(l1.e-l1.s)*k;
    }
    friend bool onseg(point p,line l) {
        return sgn((l.s-p)^(l.e-p))==0&&sgn((l.s-p)*(l.e-p))<=0;
    }
    friend int seg_to_seg(line l1,line l2) {
        if(sgn((l1.e-l1.s)^(l2.e-l2.s))==0) {
            if(onseg(l1.s,l2)||onseg(l1.e,l2)||onseg(l2.s,l1)||onseg(l2.e,l1)) return 1;
            return 0;
        }
        point inter=intersect(l1,l2);
        if(onseg(inter,l1)&&onseg(inter,l2)) return 3;
        return 2;
    }
    friend bool onright(point p,line l) {return ((l.e-l.s)^(p-l.s))<=0;}
};

point p[3];
line l[3];
struct node{int x,y,step;};
bool valid(int x,int y) {return x>=0&&x<=n-1&&y>=0&&y<=n-1&&s[x][y]!='#'&&!vis[x][y];}
int bfs() {
    memset(vis,false,sizeof(vis));
    queue<node> que;
    if(s[0][0]=='#'||s[n-1][n-1]=='#') return -1;
    que.push(node{0,0,0});
    vis[0][0]=true;
    while(!que.empty()) {
        node cur=que.front();que.pop();
        for(int i=0;i<8;i++) {
            int nxtx=cur.x+dir[i][0],nxty=cur.y+dir[i][1];
            if(valid(nxtx,nxty)&&ok[cur.x][cur.y][nxtx][nxty]) {
                vis[nxtx][nxty]=true;
                que.push(node{nxtx,nxty,cur.step+1});
                if(nxtx==n-1&&nxty==n-1) return cur.step+1;
            }
        }
    }
    return -1;

}

bool point_in_triangle(point p,point a,point b,point c) {
    return sgn(area(p,a,b)+area(p,b,c)+area(p,c,a)-area(a,b,c))==0;
}

bool zhongjian(point p,point a,point o,point b) {
    double k1=angle(b-o,p-o);
    double k2=angle(b-o,a-o);
    return sgn(k1)>0&&sgn(k1-k2)<0;
}

bool check(int a,int b,int c,int d) {
    bool ok1=a>=0&&a<=n-1&&b>=0&&b<=n-1&&c>=0&&c<=n-1&&d>=0&&d<=n-1&&s[a][b]!='#'&&s[c][d]!='#';
    if(!ok1) return false;
    point p1=point(a,b),p2=point(c,d);
    if(point_in_triangle(p1,p[0],p[1],p[2])&&point_in_triangle(p2,p[0],p[1],p[2]))  {
        bool ok=false;
        for(int i=0;i<=2;i++) if(onseg(p1,l[i])&&onseg(p2,l[i])) {ok=true;break;}
        return ok;
    }
    for(int i=0;i<=2;i++) {
        int k=seg_to_seg(line(p1,p2),l[i]);
        if(k==3){
            point inter=intersect(line(p1,p2),l[i]);
            if(!(inter==p1||inter==p2||inter==l[i].s||inter==l[i].e)) return false;
            else if(inter==p[i]) {
                if(inter==p1||inter==p2) continue;
                if(zhongjian(p1,p[((i-1)+3)%3],p[i],p[(i+1)%3])||zhongjian(p2,p[((i-1)+3)%3],p[i],p[(i+1)%3])) return false;
            }
        }
    }
    return true;
}

int main() {
    while(~scanf("%d",&n)) {
        memset(ok,false,sizeof(ok));
        for (int i = 0; i <= 2; i++) scanf("%lf %lf",&p[i].x,&p[i].y);
        if(onright(p[2],line(p[0],p[1]))) swap(p[1],p[2]);
        for(int i=0;i<=2;i++) l[i]=line(p[i],p[(i+1)%3]);
        for(int j=n-1;j>=0;j--) for(int i=0;i<=n-1;i++) scanf(" %c",&s[i][j]);
        for(int i=0;i<=n-1;i++) {
            for(int j=0;j<=n-1;j++) {
                for (int k = 0; k < 8; k++) {
                    int nxtx =i+dir[k][0],nxty=j+dir[k][1];
                    if(check(i,j,nxtx,nxty)) {
                        ok[i][j][nxtx][nxty]=ok[nxtx][nxty][i][j]=true;
                    }
                }
            }
        }
        printf("%d\n",bfs());
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值