氣球游戲-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));
}
}