最近对问题
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;
}
}