OJ分治

最近对问题

Description

最近对问题:使用分治算法解决最近对问题。

Input

第一行为测试用例个数。后面每一行表示一个用例,一个用例为一些平面上点的集合,点与点之间用逗号隔开,一个点的两个坐标用空格隔开。坐标值都是正数。

Output

对每一个用例输出两个距离最近的点(坐标使用空格隔开),用逗号隔开,先按照第一个坐标大小排列,再按照第二个坐标大小排列。如果有多个解,则按照每个解的第一个点的坐标排序,连续输出多个解,用逗号隔开。

Sample Input 1

1
1 1,2 2,3 3,4 4,5 5,1.5 1.5
Sample Output 1

1 1,1.5 1.5,1.5 1.5,2 2

思路
预处理:将点集进行排序,先比较x,再比较y
分治求解:
基解1)只有一个点,返回集合
2)只有两个点,两点之间的距离即为解
List<Point[]> nearest = new ArrayList<>(); 保存结果
1、左右区间进行划分
mid=(l+h)/h;
2、递归求最左右区间的最近对。
List<Point[]> leftNearest = getNearest(points, l, mid);
List<Point[]> rightNearest = getNearest(points, mid + 1, h);
3、比较左右区间最近对的距离。
double leftMinDistance = getMinDistance(leftNearest);
double rightMinDistance = getMinDistance(rightNearest);
三种情况:如果leftMinDistance<rightMinDistance 将左边区域的最近对保存到结果集。
如果leftMinDistance>rightMinDistance 将右边区域的最近对保存到结果集。
如果如果leftMinDistance=rightMinDistance 将两边区域的最近对保存到结果集。
4、计算一个点属于左边区域P1,一个点属于右边区域的点对距离P2,与上面距离进行比较。
将左侧X坐标到中间节点X坐标之差小于minDistance的点加入集合P1。
将右侧X坐标到中间节点X坐标之差小于minDistance的点加入集合P2。
遍历计算P1区域的点到P2区域的点之间的距离。
如果 距离小于minDistance,则重新初始化结果集,并将点对加入结果集合。
如果距离等于minDistance,则将点对加入结果集。

 public static List<Point[]> getNearest(Point[] points, int l, int h) {
        //若只有一个点,则不存在最近点对的情况
        if (l == h) {
            return new ArrayList<>();
        }
        //若只有两个点,则它们就是最近点
        if (l + 1 == h) {
            Point[] nearest = new Point[]{points[l], points[h]};
            List<Point[]> res = new ArrayList<>();
            res.add(nearest);
            return res;
        }

        //不止两个点的情况下,划分左右区间,分别取左右区间的最近点对集合
        int mid = l + ((h - l) >> 1);
        List<Point[]> leftNearest = getNearest(points, l, mid);
        List<Point[]> rightNearest = getNearest(points, mid + 1, h);

        double leftMinDistance = getMinDistance(leftNearest);
        double rightMinDistance = getMinDistance(rightNearest);

        //比较左右区间的最近点距离,更新当前区间的最近点距离以及最近点集合
        double minDistance = 0.0;
        List<Point[]> nearest = new ArrayList<>();
        if (leftMinDistance < rightMinDistance) {
            minDistance = leftMinDistance;
            nearest.addAll(leftNearest);
        } else if (leftMinDistance > rightMinDistance) {
            minDistance = rightMinDistance;
            nearest.addAll(rightNearest);
        } else {
            minDistance = leftMinDistance;
            nearest.addAll(leftNearest);
            nearest.addAll(rightNearest);
        }

        //处理最近点对一个点在左区间,另一个点在右区间的情况
        //在左区间检索到中间线距离<= minDistance的点,在右区间检索到中间线距离<= minDistance的点
        //记录检索到的点的索引
        List<Integer> leftIndex = new ArrayList<>();
        List<Integer> rightIndex = new ArrayList<>();
        for (int i = mid; i >= 0; i--) {
            if (points[mid].getX() - points[i].getX() <= minDistance) {
                leftIndex.add(i);
            } else {
                break;
            }
        }
        for (int i = mid + 1; i <= h; i++) {
            if (points[i].getX() - points[mid].getX() <= minDistance) {
                rightIndex.add(i);
            } else {
                break;
            }
        }

        for (int i = 0; i < leftIndex.size(); i++) {
            for (int j = 0; j < rightIndex.size(); j++) {
                Point leftPoint = points[leftIndex.get(i)];
                Point rightPoint = points[rightIndex.get(j)];
                //若两个点在y轴方向上的距离> minDistance,则跳过距离计算
                if (Math.abs(leftPoint.getY() - rightPoint.getY()) > minDistance) {
                    continue;
                }
                double tempDistance = distance(leftPoint, rightPoint);
                if (tempDistance < minDistance) {
                    //若距离小于当前最小距离,更新minDistance,并删除原有最近点对集合,将该点对插入
                    minDistance = tempDistance;
                    nearest = new ArrayList<>();
                    nearest.add(new Point[]{leftPoint, rightPoint});
                } else if (tempDistance == minDistance) {
                    //若距离等于当前最小距离,将点对插入最近点对集合
                    nearest.add(new Point[]{leftPoint, rightPoint});
                }
            }
        }
        return nearest;
    }

完整代码

import java.util.*;

/**
 * 分治法解最近对问题
 * Description
 * 最近对问题:使用分治算法解决最近对问题。
 * Input
 *
 * 第一行为测试用例个数。后面每一行表示一个用例,一个用例为一些平面上点的集合,点与点之间用逗号隔开,一个点的两个坐标用空格隔开。坐标值都是正数。
 *
 *  Output
 *
 * 对每一个用例输出两个距离最近的点(坐标使用空格隔开),用逗号隔开,先按照第一个坐标大小排列,再按照第二个坐标大小排列。如果有多个解,则按照每个解的第一个点的坐标排序,连续输出多个解,用逗号隔开。
 *
 *
 * Sample Input 1
 *
 * 1
 * 1 1, 2 2, 3 3, 4 4, 5 5, 1.5 1.5
 * Sample Output 1
 *
 * 1 1, 1.5 1.5,  1.5 1.5,2 2
 *  * ==============================================================================================
 */




public class Main {

    static class Point implements Comparable<Point> {
        private final String x;
        private final String y;

        public Point() {
            this.x = "0";
            this.y = "0";
        }

        public Point(String x, String y) {
            this.x = x;
            this.y = y;
        }

        public double getX() {
            return Double.parseDouble(x);
        }

        public double getY() {
            return Double.parseDouble(y);
        }

        @Override
        public String toString() {
            return this.x + " " + this.y;
        }

        @Override
        public int compareTo(Point o) {
            if (this.getX() < o.getX()) {
                return -1;
            } else if (this.getX() > o.getX()) {
                return 1;
            } else {
                if (this.getY() < o.getY()) {
                    return -1;
                } else if (this.getY() > o.getY()) {
                    return 1;
                }
            }
            return 0;
        }
    }

    public static double distance(Point p1, Point p2) {
        return Math.sqrt(Math.pow(p1.getX() - p2.getX(), 2) + Math.pow(p1.getY() - p2.getY(), 2));
    }

    //通过分治法获取最近点对集合
    public static List<Point[]> getNearest(Point[] points, int l, int h) {
        //若只有一个点,则不存在最近点对的情况
        if (l == h) {
            return new ArrayList<>();
        }
        //若只有两个点,则它们就是最近点
        if (l + 1 == h) {
            Point[] nearest = new Point[]{points[l], points[h]};
            List<Point[]> res = new ArrayList<>();
            res.add(nearest);
            return res;
        }

        //不止两个点的情况下,划分左右区间,分别取左右区间的最近点对集合
        int mid = l + ((h - l) >> 1);
        List<Point[]> leftNearest = getNearest(points, l, mid);
        List<Point[]> rightNearest = getNearest(points, mid + 1, h);

        double leftMinDistance = getMinDistance(leftNearest);
        double rightMinDistance = getMinDistance(rightNearest);

        //比较左右区间的最近点距离,更新当前区间的最近点距离以及最近点集合
        double minDistance = 0.0;
        List<Point[]> nearest = new ArrayList<>();
        if (leftMinDistance < rightMinDistance) {
            minDistance = leftMinDistance;
            nearest.addAll(leftNearest);
        } else if (leftMinDistance > rightMinDistance) {
            minDistance = rightMinDistance;
            nearest.addAll(rightNearest);
        } else {
            minDistance = leftMinDistance;
            nearest.addAll(leftNearest);
            nearest.addAll(rightNearest);
        }

        //处理最近点对一个点在左区间,另一个点在右区间的情况
        //在左区间检索到中间线距离<= minDistance的点,在右区间检索到中间线距离<= minDistance的点
        //记录检索到的点的索引
        List<Integer> leftIndex = new ArrayList<>();
        List<Integer> rightIndex = new ArrayList<>();
        for (int i = mid; i >= 0; i--) {
            if (points[mid].getX() - points[i].getX() <= minDistance) {
                leftIndex.add(i);
            } else {
                break;
            }
        }
        for (int i = mid + 1; i <= h; i++) {
            if (points[i].getX() - points[mid].getX() <= minDistance) {
                rightIndex.add(i);
            } else {
                break;
            }
        }

        for (int i = 0; i < leftIndex.size(); i++) {
            for (int j = 0; j < rightIndex.size(); j++) {
                Point leftPoint = points[leftIndex.get(i)];
                Point rightPoint = points[rightIndex.get(j)];
                //若两个点在y轴方向上的距离> minDistance,则跳过距离计算
                if (Math.abs(leftPoint.getY() - rightPoint.getY()) > minDistance) {
                    continue;
                }
                double tempDistance = distance(leftPoint, rightPoint);
                if (tempDistance < minDistance) {
                    //若距离小于当前最小距离,更新minDistance,并删除原有最近点对集合,将该点对插入
                    minDistance = tempDistance;
                    nearest = new ArrayList<>();
                    nearest.add(new Point[]{leftPoint, rightPoint});
                } else if (tempDistance == minDistance) {
                    //若距离等于当前最小距离,将点对插入最近点对集合
                    nearest.add(new Point[]{leftPoint, rightPoint});
                }
            }
        }
        return nearest;
    }

    private static double getMinDistance(List<Point[]> list) {
        if (list.size() == 0) {
            return Double.MAX_VALUE;
        }

        //取哪个点都一样,不妨取第一对点
        Point[] points = list.get(0);
        return distance(points[0], points[1]);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int t = Integer.parseInt(scanner.nextLine());
        for (int i = 0; i < t; i++) {
            String pointsString = scanner.nextLine();
            String[] pointsStrings = pointsString.split(",");
            Point[] points = new Point[pointsStrings.length];
            for (int j = 0; j < points.length; j++) {
                String[] point_str = pointsStrings[j].split(" ");
                points[j] = new Point(point_str[0], point_str[1]);
            }
            initialPoints(points);
            List<Point[]> nearest = getNearest(points, 0, points.length - 1);
            print(nearest);
        }


    }

    private static void initialPoints(Point[] points) {
        Arrays.sort(points, (o1, o2) -> {
            if (o1.getX() < o2.getX()) {
                return -1;
            } else if (o1.getX() > o2.getX()) {
                return 1;
            } else {
                if (o1.getY() < o2.getY()) {
                    return -1;
                } else if (o1.getY() > o2.getY()) {
                    return 1;
                }
            }
            return 0;  //按x轴坐标进行排序,方便后续进行分治处理
        });
    }

    private static void print(List<Point[]> nearest) {
        //将结果按第一个点x坐标进行排序
        nearest.sort((o1, o2) -> {
            if (o1[0].getX() < o2[0].getX()) {
                return -1;
            } else if (o1[0].getX() > o2[0].getX()) {
                return 1;
            }
            return 0;
        });
        for (int j = 0; j < nearest.size() - 1; j++) {
            String line = nearest.get(j)[0] + "," + nearest.get(j)[1] + ",";
            System.out.print(line);
        }
        String line = nearest.get(nearest.size() - 1)[0] + "," + nearest.get(nearest.size() - 1)[1];
        System.out.println(line);
    }
}


棋盘覆盖

棋盘覆盖问题
Description

棋盘覆盖问题:给定一个大小为2n2n个小方格的棋盘,其中有一个位置已经被填充,现在要用一个L型(22个小方格组成的大方格中去掉其中一个小方格)形状去覆盖剩下的小方格。求出覆盖方案,即哪些坐标下的小方格使用同一个L型格子覆盖。注意:坐标从0开始。左上方的第一个格子坐标为(0,0),第一行第二个坐标为(0,1),第二行第一个为(1,0),以此类推。

Input

输入第一行为测试用例个数,后面每一个用例有两行,第一行为n值和特殊的格子的坐标(用空格隔开),第二行为需要查找其属于同一个L型格子的格子坐标。

Output

输出每一行为一个用例的解,先按照行值从小到大、再按照列值从小到大的顺序输出每一个用例的两个坐标;用逗号隔开。

Sample Input 1

1
1 1 1
0 0
Sample Output 1

0 1,1 0

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

/**
 *
 */
public class Main {

    static int num=0;
    static int n;
    static int chess[][];
    static int row;
    static int col;
    static int targetx;
    static int targety;
    static Scanner in=new Scanner(System.in);
    public static   void main(String args[]){
        int T=in.nextInt();
        for(int t=0;t<T;t++){
            n=in.nextInt();//n值  2^n  乘 2^n的棋盘
            chess=new int[(int)Math.pow(2,n)][(int)Math.pow(2,n)];
            row=in.nextInt();
            col=in.nextInt();
            System.out.println("打印row和col");
            System.out.println("row:"+row);
            System.out.println("col:"+col);
            targetx=in.nextInt();
            targety=in.nextInt();
            System.out.println("targetx:"+targetx);
            System.out.println("targety:"+targety);
            int[] res=new int[4];
            int index=0;
            cover(0,0,row,col,chess.length);
           /* System.out.println("===================================打印棋盘==============================================");
            for(int i=0;i<chess.length;i++){
                for (int j=0;j<chess.length;j++){
                    System.out.print(chess[i][j]+"\t");
                }
                System.out.print("\n");
            }
            System.out.println("===================================打印棋盘==============================================");*/
            int color=chess[targetx][targety];
            for(int x=targetx-1;x<=targetx+1;x++){
                for(int y=targety-1;y<=targety+1;y++){
                    if(x>=0&&x<=chess.length-1&&y>=0&&y<=chess.length-1){
                        if(x==targety&&y==targety) continue;
                        if(color==chess[x][y]){
                            res[index++]=x;
                            res[index++]=y;
                        }
                    }
                }
            }
            System.out.println(res[0]+" "+res[1]+","+res[2]+" "+res[3]);

        }



    }

    private static void cover(int i, int j, int row, int col, int n) {

        if(n==1) return;
        int color=++num;
        int size=n/2;
        //左上角棋盘
        if(i+size>row&&j+size>col){
            //特殊的在左上角
             cover(i,j,row,col,size);
        }else{
            //左上角棋盘的右下角图上颜色
            chess[i+size-1][j+size-1]=color;
            //覆盖其余棋盘
            cover(i,j,i+size-1,j+size-1,size);
        }

        //右上角
        if(i+size>=row&&j+size>col){
            cover(i,j,row,col,size);
        }else{
            chess[i+size][j+size-1]=color;
            cover(i,j+size,i+size,j+size-1,size);
        }

        //左下角

        if(i+size>row&&j+size<=col){
            cover(i+size,j,row,col,size);
        }else{
            chess[i+size-1][j+size]=color;
            cover(i+size,j,i+size-1,j+size,size);
        }

        //右下角
        if(i+size<=row&&j+size<=col){
            cover(i+size,j+size,row,col,size);
        }else{
            chess[i+size][j+size]=color;
            cover(i+size,j+size,i+size,j+size,size);
        }
    }


}

点的凸包

点的凸包
Description

Convex Hull of a set of points, in 2D plane, is a convex polygon with minimum area such that each point lies either on the boundary of polygon or inside it. Now given a set of points the task is to find the convex hull of points.

Input

The first line of input contains an integer T denoting the no of test cases. Then T test cases follow. Each test case contains an integer N denoting the no of points. Then in the next line are N*2 space separated values denoting the points ie x and y.Constraints:1<=T<=100,1<=N<=100,1<=x,y<=1000

Output

For each test case in a new line print the points x and y of the convex hull separated by a space in sorted order (increasing by x) where every pair is separated from the other by a ‘,’. If no convex hull is possible print -1.

Sample Input 1

2
3
1 2 3 1 5 6
3
1 2 4 4 5 1
Sample Output 1

1 2, 3 1, 5 6
1 2, 4 4, 5 1

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

public class Main{
    static class Point {
        private int x, y;// 横纵坐标

        public Point(int x,int y) {
            this.x=x;
            this.y=y;
        }

        public String toString() {
            return x + " "+ y;
        }

    }
    public static  void main(String [] args){
        Scanner in=new Scanner(System.in);
        int T=in.nextInt();
        while(T-- >0){
            int N=in.nextInt();
            if(N==0){
                System.out.println("-1");
                continue;
            }else {
                Point[] points = new Point[N];
                for (int i = 0; i < N; i++) {
                    points[i] = new Point(in.nextInt(), in.nextInt());
                }
                ConvexHullProblem(points);
            }
        }
    }


    static public void ConvexHullProblem(Point[] points) {
        if(points.length<=2) {
            System.out.println(-1);
            return;
        }
        boolean isLine=true;
        for(int i=2;i<points.length;i++){
           if(PointCal(points[0],points[1],points[i])!=0) {isLine=false; break;}
        }
        if(isLine){
            System.out.println(-1);
            return;
        }
        points = points;
        Arrays.sort(points, new Comparator<Point>() {
            @Override
            public int compare(Point o1, Point o2) {
                if(o1.x==o2.x) return o1.y-o2.y;
                return o1.x-o2.x;
            }
        });
        boolean[] visit = new boolean[points.length];
        recursion(0, points.length - 1,points,visit);// 上包
        recursion(points.length - 1, 0,points,visit);// 下包
        boolean flag=false;
        int last=-1;
        for(int i=0;i<visit.length;i++){
            flag|=visit[i];//只要有一个值为true则为true
            if(visit[i]) last=i;

        }
        if(!flag) {System.out.println(-1); return ;}

        for(int i=0;i<visit.length;i++){
            if(visit[i]){
                System.out.print(points[i]);
                if(i!=last) System.out.print(", ");
            }

        }

        System.out.println("");

    }

    /**
     * @title: recursion
     * @description: 在凸包的上包或下包中,找使△p_max p1 p_n面积最大的点p_max,并递归
     * @param begin 直线的起点
     * @param end   直线的终点 void
     * @throws:
     */
    static void recursion(int begin, int end,Point[] points,boolean[] visit) {
        // 直线的两端点为凸包的顶点
        visit[begin] = true;
        visit[end] = true;
        //将该条直线上的点设置为true;

        if (begin < end) {
            boolean flag = false;// 标志直线左侧是否有点
            int maxArea = 0;// 最大面积
            int maxAreaindex = begin + 1;// 最大面积的点下标

            for (int i = begin + 1; i <= end - 1; i++) {// begin和end已经是顶点,不需要判断
                int dis=PointJudge(points[begin], points[end], points[i]);
                if (dis==0) visit[i]=true;
                if (dis > 0) {// 点在直线左侧
                    // 找距离最远的点,因为底相同都是p1 pn,也就是求三角形面积最大的
                    flag = true;
                    int area = PointCal(points[begin], points[end], points[i]);
                    if (area > maxArea) {
                        maxArea = area;
                        maxAreaindex = i;
                    } else if (area == maxArea) {// 若面积相同,取∠p_max p_begin p_end最大的那一个
                        double degreeA = Degree(points[begin], points[i], points[end]);
                        double degreeB = Degree(points[begin], points[maxAreaindex], points[end]);
                        if (degreeA > degreeB) {

                            maxArea = area;
                            maxAreaindex = i;
                        }
                    }

                }
            }

            // 若直线左侧还有点,则递归;没有点,则结束
            if (flag == true) {
                recursion(begin, maxAreaindex,points,visit);
                recursion(maxAreaindex, end,points,visit);
            }
        } else if (begin > end) {
            boolean flag = false;
            int maxArea = 0;// 最大面积
            int maxAreaindex = end + 1;// 最大面积的点下标

            for (int i = begin - 1; i >= end + 1; i--) {// 注意下包循环中的 起始点、终点、判断条件
                int dis=PointJudge(points[begin], points[end], points[i]);
                if (dis==0) visit[i]=true;
                if (dis > 0) {// 点在直线左侧
                    flag = true;
                    int area = PointCal(points[begin], points[end], points[i]);
                    if (area > maxArea) {
                        maxArea = area;
                        maxAreaindex = i;
                    } else if (area == maxArea) {// 若面积相同,取∠p_max p_begin p_end最大的那一个
                        double degreeA = Degree(points[begin], points[i], points[end]);
                        double degreeB = Degree(points[begin], points[maxAreaindex], points[end]);
                        visit[maxAreaindex]=true;
                        visit[i]=true;
                        if (degreeA > degreeB) {
                            maxArea = area;
                            maxAreaindex = i;
                        }
                    }

                }
            }

            if (flag == true) {
                recursion(begin, maxAreaindex,points,visit);
                recursion(maxAreaindex, end,points,visit);
            }
        }
    }


    /**
     * @title: PointCal
     * @description: 计算行列式的值
     * @param beginP 直线的开始点
     * @param p      判断的点
     * @param endP   直线的终点
     * @throws:
     */
    static private int PointCal(Point beginP, Point endP, Point p) {
        return beginP.x * endP.y + p.x * beginP.y + endP.x * p.y - p.x * endP.y
                - endP.x * beginP.y - beginP.x * p.y;
    }

    /**
     * @title: PointJudge
     * @description:返回点p在直线beginP endP的位置
     * @param beginP
     * @param p      判断的点
     * @param endP
     * @return int :1在直线左侧,0在线上,-1在右侧
     * @throws: 注意传参放在第几个,前两个点是直线的两端,第三个是需要判断的点
     */
    static  private int PointJudge(Point beginP, Point endP, Point p) {
        if (PointCal(beginP, endP, p) > 0) {
            return 1;
        } else if (PointCal(beginP, endP, p) == 0)
            return 0;
        else
            return -1;
    }

    /**
     * @title: Degree
     * @description: 余弦公式求∠pa pb pc的度数
     * @param pa 点
     * @param pb
     * @param pc
     * @return double:返回∠c的度数(°为单位)
     * @throws:
     */
    static  double Degree(Point pa, Point pb, Point pc) {
        double degree = 0;// ∠pa pb pc度数

        // 三角形的三边长
        double a = Math.sqrt(Math.pow(pa.x - pb.x, 2) + Math.pow(pa.y - pb.y, 2));
        double b = Math.sqrt(Math.pow(pb.x - pc.x, 2) + Math.pow(pb.y - pc.y, 2));
        double c = Math.sqrt(Math.pow(pc.x - pa.x, 2) + Math.pow(pc.y - pa.y, 2));

        // 余弦公式求∠pa pb pc度数
        System.out.println("acos=" + Math.acos((a * a + b * b - c * c) / (2.0 * a * b)));
        degree = Math.toDegrees(Math.acos((a * a + b * b - c * c) / (2.0 * a * b)));
        System.out.println("degree=" + degree);

        return degree;
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值