判斷矩形和圓交


一.算法

        方法一:先判斷矩形是否在圓內(矩形的四個頂點是否在圓內),若是則不相交,否則再判斷圓心到矩形四條邊的最短距離(點到線段的最短距離)是否存在小於半徑的,若是則相交(認為矩形包括圓是不相交的,已經先排除了)。方法二:圓分平面為四部分,

        方法二:圓分平面四部分,不相交的情況分了幾種:長方形在圓形上面,長方形在圓形下面,長方形在圓形左邊,長方形在圓形右邊,長方形在圓形內部,圓形在長方形內部。

        方法三:矩形分平面九部分,用矩形的四個邊,把空間划分成為9個區域,判定圓心的位置在那個區域當中,如果在矩形的內部,則必然的相交,如果位於上下左右四個邊區域當中,檢測圓心到邊的距離,判定是否相交,如果位於四個角點對應的區域,只要檢測矩形的四個角是否在圓的內部就是了。

        錯誤做法:

  • 圓在矩形內或者矩形在圓內都不算相交,假設對角線是左下角和右上角(目測是這樣,不是也沒關系),若圓心不在橫縱坐標范圍內那么肯定不交,這種想法錯誤,想想矩形在圓右下角,看下圖。

image

  • 這樣判斷矩形在圓內不對,看下圖
p.y+r>ymax&&p.y-r>ymin&&p.x-r>xmin&&p.x+r>xmax

image

 

        不得不說做這道題收獲不小……

二.算法實現

        以HDU1221為例,直接去AC吧。

import java.util.Scanner;
//AC了
public class W {
  public static void main(String[] args) {
    int T;
    double x,y;
    double r;
    Scanner sc = new Scanner(System.in);
    T = sc.nextInt();
    while(T-->0) {
      x = sc.nextDouble();
      y = sc.nextDouble();
      //圓心
      PointW p = new PointW(x,y);
      r = sc.nextDouble();
      x = sc.nextDouble();
      y = sc.nextDouble();
      PointW p1 = new PointW(x,y);
      x = sc.nextDouble();
      y = sc.nextDouble();
      PointW p2 = new PointW(x,y);
      boolean tag = go(p,r,p1,p2);
      if(tag) {
        System.out.println("YES");
      }else {
        System.out.println("NO");
      }
    }
  }
  private static boolean go(PointW p, double r, PointW p1, PointW p2) {
    /*
     * 為節省內存也可以只用兩個點,不要Point類,x1,y1存儲xy小值,然后排列組合就得到四個點了
     */
    double xmin = Math.min(p1.x,p2.x);
    double xmax = p1.x + p2.x - xmin;
    double ymin = Math.min(p1.y,p2.y);
    double ymax = p1.y + p2.y - ymin;
    //矩形四點;從左下角向上、向右,再向下
    PointW q1 = new PointW(xmin,ymin);
    PointW q2 = new PointW(xmin,ymax);
    PointW q3 = new PointW(xmax,ymax);
    PointW q4 = new PointW(xmax,ymin);
    boolean i = Double.compare(distance(p, q1), r)<0;
    boolean j = Double.compare(distance(p, q2), r)<0;
    boolean k = Double.compare(distance(p, q3), r)<0;
    boolean t = Double.compare(distance(p, q4), r)<0;
    //在圓內可以這樣算,在圓外不能簡單地把小於0改成大於0,考慮矩形貫穿圓
    if(xmax<p.x-r||ymin>p.y+r||xmin>p.x+r||ymax<p.y-r) {
      return false;
    }else if(i&&j&&k&&t) {
      return false;
    }else if(p.y+r<ymax&&p.y-r>ymin&&p.x-r>xmin&&p.x+r<xmax){//在矩形內
        return false;
    }else {
      return true;
    }
  }
  private static double distance(PointW p, PointW p1) {
    return Math.hypot(p.x-p1.x, p.y-p1.y);
  }
}
class PointW {
  double x;
  double y;
  public PointW() {
    this.x = 0;
    this.y = 0;
  }
  public PointW(double x, double y) {
    this.x = x;
    this.y = y;
  }
}

        下面的wa了,路過的給瞧一瞧。

import java.util.Scanner;
//wa
public class HDU1221 {
  public static void main(String[] args) {
    int T;
    double x,y;
    double r;
    Scanner sc = new Scanner(System.in);
    T = sc.nextInt();
    while(T-->0) {
      x = sc.nextDouble();
      y = sc.nextDouble();
      //圓心
      Point p = new Point(x,y);
      r = sc.nextDouble();
      x = sc.nextDouble();
      y = sc.nextDouble();
      Point p1 = new Point(x,y);
      x = sc.nextDouble();
      y = sc.nextDouble();
      Point p2 = new Point(x,y);
      boolean tag = go(p,r,p1,p2);
      if(tag) {
        System.out.println("YES");
      }else {
        System.out.println("NO");
      }
    }
  }
  private static boolean go(Point p, double r, Point p1, Point p2) {
    double xmin = Math.min(p1.x,p2.x);
    double xmax = p1.x + p2.x - xmin;
    double ymin = Math.min(p1.y,p2.y);
    //原來ymin寫成了xmin
    double ymax = p1.y + p2.y - ymin;
    //矩形四點;從左下角向上、向右,再向下
    Point q1 = new Point(xmin,ymin);
    Point q2 = new Point(xmin,ymax);
    Point q3 = new Point(xmax,ymax);
    Point q4 = new Point(xmax,ymin);
    boolean i = Double.compare(distance(p, q1), r)<0;
    boolean j = Double.compare(distance(p, q2), r)<0;
    boolean k = Double.compare(distance(p, q3), r)<0;
    boolean t = Double.compare(distance(p, q4), r)<0;
    if(i&&j&&k&&t) {//先排除在圓內情況,采用if else
      return false;
    }else {
      //等於0表示相切(tangent)
      i = Double.compare(pointToLine(q1,q2,p), r)<=0;
      j = Double.compare(pointToLine(q2,q3,p), r)<=0;
      k = Double.compare(pointToLine(q3,q4,p), r)<=0;
      t = Double.compare(pointToLine(q4,q1,p), r)<=0;
      if(i||j||k||t) {
        return true;
      }else {
        return false;
      }
    }
  }
  private static double distance(Point p, Point p1) {
    return Math.hypot(p.x-p1.x, p.y-p1.y);
  }
  //點到線段的最短距離,x0,y0是圓心
  private static double pointToLine(Point p1,Point p2, Point p) {
    double ans = 0;
    double a, b, c;
    a = distance(p1, p2);
    b = distance(p1, p);
    c = distance(p2, p);
    if (c+b==a) {//點在線段上
      ans = 0;
      return ans;
    }
    if (a<=1e-8) {//不是線段,是一個點
      ans = b;
      return ans;
    }
    if (c*c >= a*a + b*b) { //組成直角三角形或鈍角三角形,p1為直角或鈍角
      ans = b;
      return ans;
    }
    if (b * b >= a * a + c * c) {// 組成直角三角形或鈍角三角形,p2為直角或鈍角
      ans = c;
      return ans;
    }
    // 組成銳角三角形,則求三角形的高
    double p0 = (a + b + c) / 2;// 半周長
    double s = Math.sqrt(p0 * (p0 - a) * (p0 - b) * (p0 - c));// 海倫公式求面積
    ans = 2*s / a;// 返回點到線的距離(利用三角形面積公式求高)
    return ans;
  }
}
class Point {
  double x;
  double y;
  public Point() {
    this.x = 0;
    this.y = 0;
  }
  public Point(double x, double y) {
    this.x = x;
    this.y = y;
  }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM