华为OD题目:区间连接器
知识点数组排序Q滑窗
时间限制: 1s 空间限制: 256MB 限定语言: 不限
题目描述
有一组区间[a0,b0],[a1,b1],…(a,b 表示起点,终点),区间有可能重叠、相邻,重鲁或相邻则可以合并为更大的区间:
给定一组连接器[x1,x2,x3,.] (x 表示连接器的最大可连接长度,即 x>=gap),可用于将分离的区间连接起来,但两个分离区间之间只能使用1个连接器;
请编程实现使用连接器后,最少的区间数结果。
区间数量 <10000: a,b 均 <=10000
连接器梳理 <10000:x <=10000
输入描述:
区间组: [1,10],[15,20],[18,30],[33,40]
连接器组: [5,4,3,2]
输出描述:
说明: 合并后: [1,10],[15,30],[33,40],使用 5,3 两个连接器连接后只剩下[1,40]
示例1
输入:
[1,10],[15,20],[18,30],[33,40]
[5,4,3,2]
输出:
1
说明:
合并后: [1,10],[15,30],[33,40],使用 5,3 两个连接器连接后只剩下[1,40]
示例2
输入:
[1,2],[3,5],[7,10],[15,20],[30,100]
[5,4,3,2,1]
输出:
2
说明:
无重叠和相邻,使用 1,2, 5 三个连接器连接后只剩下[1,20],[30,100]
解题思路:
1、对区间进行升序排序:
2将相邻和存在交集的区间进行合并
3求得步骤2中各区间的距离集合
4将步骤3的集合与连接器集合进行比较 (使用双指针进行比较)
注:使用过的连接器不能继续使用:连接器取最接近的
public class My {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
String[] regionsStr = line.replaceAll("\\[","")
.replaceAll("]","")
.split(",");
//区间集合
List<int[]> list = new ArrayList<>();
//组装数据
for( int i=0; i<regionsStr.length; i+=2){
int left = Integer.parseInt(regionsStr[i]);
int right = Integer.parseInt(regionsStr[i+1]);
list.add(new int[]{ left, right});
}
String linkStr = sc.nextLine();
//连接器数组
int[] linkArr = getArr(linkStr);
//对list按照左侧进行升序排列
list.sort((a1, a2) -> a1[0] - a2[0]);
//对连接器进行升序排列
Arrays.sort(linkArr);
//先将区间进行合并
List<int[]> mergedList = new ArrayList<>();
int[] pre = list.get(0);
mergedList.add(pre);
for (int i = 1; i < list.size(); i++) {
int[] currRegion = list.get(i);
//如果当前区域左端点 <- 已经合并的右端点,那么可以把当前节点并入进去
if (currRegion[0] <= pre[1]) {
pre[1] = currRegion[1];
}else {
//否则,合并不了
mergedList.add(currRegion);
pre = currRegion;
}
}
//初始化第一个连接区域
int[] preRegion = mergedList.get(0);
int[] gapArr = new int[mergedList.size() - 1];
for (int i = 1, j = 0; i < mergedList.size(); i++, j++) {
int[] region = mergedList.get(i);
//左端点减去前面区域的右端点
int gap = region[0] - preRegion[1];
gapArr[i - 1] = gap;
preRegion = region;
}
Arrays.sort(gapArr);
//获取使用了连接器的个数
int usedLinkNum = getUsedLink(gapArr, linkArr);
//usedLinkNum个连接器可以连接 usedLinkNum+1个区域
int remain = mergedList.size() - usedLinkNum;
System.out.println(remain);
}
public static int[] getArr(String str) {
String replace1 = str.replace("[", "");
String replace2 = replace1.replace("]", "");
String[] split = replace2.split(",");
int[] arr = new int[split.length];
for (int i = 0; i < arr.length; i++) {
arr[i] = Integer.parseInt(split[i]);
}
return arr;
}
//获取使用了的连接器的数量
public static int getUsedLink(int[] gapArr, int[] linkArr) {
//用i, j两个指针分别指gap数组和连接器数组的下标
int i = 0;
int j = 0;
int usedNum = 0;
while (i < gapArr.length && j < linkArr.length) {
if (linkArr[j] >= gapArr[i]) {
System.out.println(" used link=" + linkArr[j]);
i++;
j++;
usedNum++;
}else {
j++;
}
}
return usedNum;
}
}