【百度百科】分治法((Divide and Conquer))可以通俗的解释为:把一片领土分解,分解为若干块小部分,然后一块块地占领征服,被分解的可以是不同的政治派别或是其他什么,然后让他们彼此异化。
分治法的精髓:
在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)……
大白话:大事化小,小事化了。
上述的第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
大白话:简单问题分治法,复杂问题贪心法或动态规划,是否简单看上面的四个特征↑。
由分治法产生的子问题往往是原问题的较小模式,这就可以使用递归技术来解决。分治与递归像一对孪生兄弟,经常同时应用在算法设计之中,并由此产生许多高效算法。
这样,我们就把分治法化为了怎么进行递归。
这三种方法分别是:代入法,递归树法,和主方法。代入法是一种十分强大的方法,它几乎可以求解所有的递归式。然而,对于一些“小鸡”来说,不需要这么强大的“牛刀”。对于一些特定类型的递归式(具体类型在主方法的小节中会介绍),用主方法可以用更少的时间得到一个更确切的界。
题目:假设所有点都在集合S中。
import java.util.*;
public class S2_4 {
public static void main(String[] args) {
Scanner s=new Scanner(System.in);
int x=0,x1=0,x2=0,x3=0,x4=0;
int y=0,y1=0,y2=0;
double dis1=0,dis2=0;
System.out.print("输入需要生成多少个随机点N:");
int n=s.nextInt();
int A[][]=new int[n][2];
int B[][]=new int[n][2];
int C[][]=new int[n][2];
int D[][]=new int[n][2];
for(int i=0;i<n;i++) {
A[i][0]=(int)(Math.random()*100)+1;//生成100以内的随机数,放入横坐标
}
for(int i=0;i<n;i++) {
A[i][1]=(int)(Math.random()*100)+1;//生成100以内的随机数,放入纵坐标
}
for(int i=0;i<n;i++) {
System.out.println("("+A[i][0]+","+A[i][1]+")");
}
int minX = (int) Double.POSITIVE_INFINITY; //保证假设的初始最小值足够大
int maxX = (int) Double.NEGATIVE_INFINITY; //保证假设的初始最大值足够小
for(int i = 0; i < A.length; i++){
if(A[i][0] < minX)
minX = A[i][0];
if(A[i][0] > maxX)
maxX = A[i][0];
}
int mid = (minX + maxX)/2;
int p=0,t=0;
for(int i=0;i<n;i++) { //将x坐标分成俩个部分
if(A[i][0]<=mid) {
B[p][0]=A[i][0];
B[p][1]=A[i][1];
p++;
}
else {
C[t][0]=A[i][0];
C[t][1]=A[i][1];
t++;
}
}
int d1=(int) Double.POSITIVE_INFINITY,d2=(int) Double.POSITIVE_INFINITY; //设置俩边的距离最小值为较大的数值
int dx=0,dy=0,dz=0;
for(int i=0;i<=p-2;i++) //得出d1的值
for(int j=i+1;j<=p-1;j++) {
dx=(B[j][0]-B[i][0])*(B[j][0]-B[i][0])+(B[j][1]-B[i][1])*(B[j][1]-B[i][1]);
if(dx<d1) {
d1=dx;
x1=i; //记录俩个点的坐标
x2=j;
}
}
for(int i=0;i<=t-2;i++) //得出d2的值
for(int j=i+1;j<=t-1;j++) {
dy=(C[j][0]-C[i][0])*(C[j][0]-C[i][0])+(C[j][1]-C[i][1])*(C[j][1]-C[i][1]);
if(dy<d2) {
d2=dy;
x3=i; //记录俩个点的坐标
x4=j;
}
}
System.out.println("mid="+mid+" "+"d1="+d1+" "+"d2="+d2);
if(d1<d2) {
dz=d1;
dis1=Math.sqrt(dz);
System.out.println("x坐标中的最小距离的俩个点为:"+A[x1][0]+","+A[x1][1]+" "+A[x2][0]+","+A[x2][1]);
System.out.println("最小距离为:"+dis1);
x=x1;
y=x2;
}else {
dz=d2;
dis1=Math.sqrt(dz);
System.out.println("x坐标中的最小距离的俩个点为:"+A[x3][0]+","+A[x3][1]+" "+A[x4][0]+","+A[x4][1]);
System.out.println("最小距离为:"+dis1);
x=x3;
y=x4;
}
int q=0;
for(int i=0;i<n;i++) {
if((mid-dis1)<=A[i][0] && A[i][0]<=(mid+dis1)) { //中心线左右俩边的最小距离内寻找俩边的最近点
D[q][0]=A[i][0];
D[q][1]=A[i][1];
q++;
}
}
double mind=Double.POSITIVE_INFINITY;//mind设置为正无穷大,作为比较值
double dis=0;
for(int k=0;k<=q-2;k++)
for(int j=k+1;j<=q-1;j++) {
dis=(D[j][0]-D[k][0])*(D[j][0]-D[k][0])+(D[j][1]-D[k][1])*(D[j][1]-D[k][1]);
if(dis<mind) {
mind=dis;
y1=k; //记录俩个点的坐标
y2=j;
}
}
dis2=Math.sqrt(mind);
System.out.println("dis1="+dis1+" "+"dis2="+dis2);
if(dis1<dis2) {
System.out.println("最小距离为:"+dis1);
System.out.print("俩个点分别为:"+"("+A[x][0]+","+A[x][1]+")");
System.out.println(" "+"("+A[y][0]+","+A[y][1]+")" );
}else {
System.out.println("最小距离为:"+dis2);
System.out.print("俩个点分别为:"+"("+A[y1][0]+","+A[y1][1]+")");
System.out.println(" "+"("+A[y2][0]+","+A[y2][1]+")" );
}
}
}
我的微信公众号:架构真经(id:gentoo666),分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。每日更新哦!
参考资料:
原文:https://www.cnblogs.com/anymk/p/11521503.html