package arithmetic;
import java.util.*;
/*
* 定义线段类 left 左座标 right 右座标
* */
class Line
{
int left;
int right;
public Line()
{}
public Line(int left,int right)
{
this.left = left;
this.right = right;
}
}
public class LineOver {
public static void main(String args[])
{
long begin=System.currentTimeMillis();
over();
System.out.println("over cost: "+runTime(begin));
begin = System.currentTimeMillis();
bigOver();
System.out.println("bigOver cost: "+runTime(begin));
}
/*
* 用线段覆盖的方法:
* 建立一个 数据 a[min - max-1] min 表示所有点的最小的座标,max 表示最大 的座标 ,初始值设置为0 a[i] 的值表示是的i -i+1 点的距离
* 对于所有线段遍历,如果线段上段 在数组上,那么数组上的值就设置为1 ,这样所有1 组成的线段就是上面所有线段覆盖的总长度
*
*
* 此方法的时间复杂度决定于下标范围的平方。
当下标范围很大时([0,10000]),此方法效率太低。
* */
public static void over()
{
Line line[] = {new Line(10000,22000),new Line(30300,55000),new Line(44000,60000),new Line(5500000,6000000)};
int min=line[0].left,max=line[0].right;
for(int i =1;i<line.length;i++) //求出线段的最小端点和最大端点
{
min = Math.min(min,line[i].left);
max = Math.max(max,line[i].right);
}
/*
* 找到最大值时,下面建立数组
* */
int a [] = new int [max]; //以最大端点和最小端点为数组创建一个数组,而座标的范围是 [min-max]
//注意这个数组表示的意义:每一个元素 a[i] 表示 i -i +1 的范围 //将数组的初始值为0,如果线段覆盖的话,再将其值设置为1
for(int i =0;i<line.length;i++)//下面遍历所有线段
for(int j=line[i].left;j<line[i].right;j++) //如果一个线段的范围是 [2-4] 那么对应的数组里面的 a[2] a[3] 的值都将会是1(注意:a[4]的值仍然为0,因为a[4]表示的是[4-5]的线段)
a[j]=1;
int sum=0;
for(int i =0;i<max;i++) //这样再统计数组内1的个数,那个所有线段覆盖的范围就可以求出来了
if(a[i]==1)
sum+=1;
System.out.println(sum);
/*
* 该方法就是利用一个数组作为一个线段,把要求的线段都贴到数组线段上去,然后求得被覆盖的线段的总长度即可,
* 然后上面的方法只适用于比较小的数据,而数据较大时,那么就要创建一个一样大的数组,并且这种方法的效率大低
*
* */
}
/*
* 先说一下原理
* 1. 求出所有线段的端点,再将端点进行一次排序10000,22000,30300 44000 55000 60000
*
* 2. 对所有的线段进行缩放,比如[10000,22000],它的两个端点分别是上面 上面端点的第一个和第二个,那么就可以表示成 [1-2]
* 同理,将所有的端点都这样表示,即,把问题又回到了上面一个,然后再用同样的方法解决
* 3. 创建一个数组,两样初始值都是0,对于 a[i] 表示 i - i+1 的线段,
* 4. 对数组内所有值为1的进行还原,注意:这时a [i] 中i 对应用的和上面进行排序的数组是一个的,对a [0] 就表示 10000-22000
* 那么即可以求出所以线段的覆盖范围
*
*
* 此方法的时间复杂度决定于线段数的平方。
对于线段数较多的情况此方法效率太低。
*
* */
public static void bigOver()
{
Line line[] = {new Line(10000,22000),new Line(30300,55000),new Line(44000,60000),new Line(5500000,6000000)};
ArrayList<Integer> list = new ArrayList<Integer>();
for(int i=0;i<line.length;i++)
{
if(!list.contains(line[i].left))
list.add(line[i].left);
if(!list.contains(line[i].right))
list.add(line[i].right);
}
Collections.sort(list); //将所有的端点求出并对其进行排序
//重新构造线段
int a [] = new int[list.size()];// Java 中数组的初始值为0 ,所以不要再为其 赋值0
for(int i=0;i<line.length;i++)
{
int left=list.indexOf(line[i].left);// 找出其端点,然后再在对应的 a [] 数组中设置相应的0 or 1
int right = list.indexOf(line[i].right);
for(int j = left;j<right;j++)
a[j]=1;
}
int sum=0;
for(int i =0 ;i<a.length;i++)
if(a[i]==1)
sum+=(list.get(i+1)-list.get(i));//注意:此时的a 数组是和 list 对应的,a [1] 就是表示 list.get(2)-list.get(1),这样很重要
System.out.println(sum);
}
public static long runTime(long begin)
{
return System.currentTimeMillis()-begin;
}
}