气球游戏-2019腾讯笔试
小Q在进行射击气球的游戏,如果小Q在连续T枪中打爆了所有颜色的气球,将得到一只QQ公仔作为奖励。(每种颜色的球至少被打爆一只)。
这个游戏中有m种不同颜色的气球,编号1到m。
小Q一共有n发子弹,然后连续开了n枪。
小Q想知道在这n枪中,打爆所有颜色的气球最少用了连续几枪?
输入格式
第一行包含两个整数n
和m
。
第二行包含n
个整数,分别表示每一枪打中的气球的颜色,0
表示没打中任何颜色的气球。
输出格式
一个整数表示小Q打爆所有颜色气球用的最少枪数。
如果小Q无法在这n
枪打爆所有颜色的气球,则输出-1
。
数据范围
1≤n≤\(10^6\),
1≤m≤2000
输入样例:
12 5
2 5 3 1 3 2 4 1 0 5 4 3
输出样例:
6
样例解释
有五种颜色的气球,编号1
到5
。
游客从第二枪开始直到第七枪,这连续六枪打爆了5 3 1 3 2 4
这几种颜色的气球,包含了从1
到5
的所有颜色,所以最少枪数为6
。
解法一:双指针算法
贪心思想,用指针i
,j
维护区间[i, j]
,移动j
扩大区间使得区间内包含所有颜色气球,当满足时,移动i
指针使得[i,j]
区间变小依然能满足包含所以颜色的气球,此时更新一次答案(区间大小);直到遍历完整个数组。
i
,j
都只增不减,因此时间复杂度为O(n)
;需要维护区间内每种气球的数量,以及一个HashSet
快速判断区间内气球的种类数量,或者直接使用HashMap
,因此空间复杂度为O(m)
。
import java.util.*;
public class Main {
static int search(int[] a, int n, int m) {
int[] cnt = new int[m+1];
Set<Integer> set = new HashSet<>();
int res = -1;
int i = 0, j = 0;
while(i <= j && j < n) {
cnt[a[j]]++;
if(a[j] != 0) set.add(a[j]);
if(set.size() >= m) {
while(i < j) {
if(a[i] != 0 && cnt[a[i]] == 1)
break;
cnt[a[i]]--;
i++;
}
if(res == -1 || j-i+1 < res) res = j-i+1;
}
// System.out.println(i+"---"+j);
j++;
}
return res;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[] a = new int[n];
for(int i=0; i< n; i++)
a[i] = sc.nextInt();
System.out.println(search(a, n, m));
}
}
解法二、二分查找(较为暴力)
如果没想到第一种方法,则可以暴力做。显然,答案一定在区间[m,n]
内,二分查找答案,时间复杂度为O(log(n-m))
。判断k
是否为答案时间复杂度为O(n)
,因此整个程序时间复杂度O(nlogn)
。大致计算\(10^6 \times \log_2^{10^6}\approx 10^6 \times log_2^{2^{20}}=2 \times 10^7\)
时间上依然可以接受;空间复杂度同上。
import java.util.*;
public class Main {
static int search(int[] a, int l, int r){
int m=l, n = r;
Set<Integer> set = new HashSet<>();
int[] cnt = new int[m+1];
for(int i=0; i < n; i++)
if(a[i] != 0)
set.add(a[i]);
if(set.size() < m) return -1;
while(l < r) {
int mid = (l+r)/2;
set.clear();
Arrays.fill(cnt,0);
for(int i=0; i < n; i++) {
if(a[i] != 0 && !set.contains(a[i]))
set.add(a[i]);
cnt[a[i]]++;
if(i >= mid) {
cnt[a[i-mid]]--;
if(a[i-mid] != 0 && cnt[a[i-mid]]<= 0)
set.remove(a[i-mid]);
}
if(set.size() >= m) break;
}
if(set.size() >= m)
r=mid;
else
l=mid+1;
}
return l;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[] a = new int[n];
for(int i=0; i < n; i++)
a[i] = sc.nextInt();
System.out.println(search(a, m, n));
}
}