问题
假设有不同颜色的气球用数字标识 n = 5, [1,2,3,4,5]
玩家可以用枪打破气球序列为 m = 11,[1,2,2,3,4,5,2,2,0,2,1],0为未击中
如果连续击中所有不同颜色气球,可以获得奖品,求最短获奖路径
示例
m = 11,n = 5,arr = [1,2,2,3,4,5,2,1,0,2,1]
有五种气球,玩家一共开了11枪
有效序列[1,2,2,3,4,5]长度为6
有效序列[3,4,5,2,1]长度为1
如果没有有效序列则返回-1
思路
- 找到一个有效序列,确定移动窗口大小,数组记录窗口位置
- 通过队列入队出队移动窗口,判断窗口是否合法,如果合法保留最小值,更新位置数组
- 如果空枪,重置窗口和位置数组
- 如果遇到空枪之前已确定窗口,可以直接移动窗口步长
题解
public static void main(String[] args) {
int m = 11;
int n = 5;
int[] arr = {1,2,2,3,4,5,2,2,0,2,1};
System.out.println(hit(arr,m,n));
}
private static int hit(int[] arr,int m,int n){
int[] index = new int[n+1];
Queue<Integer> queue = new LinkedList();
for(int i = 0;i<index.length;i++){
index[i] = -1;
}
int count = 0;
int res = m + 1;
for(int i = 0;i<arr.length;i++){
if(arr[i] != 0){
//之前未中,记录队列入队
if (index[arr[i]] == -1){
index[arr[i]] = i;
queue.add(arr[i]);
count++;
//第一次全中,确定窗口大小
if (count == n){
res = index[i] - index[queue.peek()] + 1;
continue;
}
}
//已经全中,判断窗口是否正确,如果正确返回最小值
if (count == n){
queue.poll();
queue.add(arr[i]);
//队列满足条件
if(check(queue,n)){
int len = i - index[queue.peek()] + 1;
res = Math.min(res,len);
}
//更新当前元素位置
index[arr[i]] = i;
}
}else {
//未打中重置
count = 0;
index = new int[n+1];
queue = new LinkedList();
}
}
return res == m + 1 ? -1:res;
}
//校验窗口是否有效
private static boolean check(Queue<Integer> queue,int n){
int count = 0;
boolean[] isHit = new boolean[n + 1];
for(Integer num:queue){
if (!isHit[num]){
isHit[num] = true;
count++;
}
}
return count == n;
}