HDU-2158 最短區間版大家來找茬 模擬 尺取
阿新 • • 發佈:2020-08-21
HDU-2158 最短區間版大家來找茬 模擬 尺取
題意
給定一個長度為\(N\) 的序列,序列中每個整數的範圍是\([0,N)\)
給出\(M\) 個詢問,每次詢問給出一個整數\(Q\) ,接下來有\(Q\) 個整數,這\(Q\) 個整數可能包含重複值。
現要找出一個最短的區間包含這\(Q\) 個整數。輸出最小的區間長度
\[N < 100000,M < 1000,Q < 100 \]
分析
顯然是個模擬題。
暴力必然是不可取的,考慮每個詢問O(N)的做法,那可以剛剛好過掉這題。
方法是尺取,相當於給定一個標杆\(l\) 和\(r\) ,從左到右列舉\(l\) ,對於每個\(l\) ,\(r\)必須滿足條件為止,滿足條件後維護最小值。
思路想起來容易實現起來難:
1.如何判斷是否滿足了條件
2.如何在移動的時候維護資訊
由於題目的特殊性質,序列可以是不同的數,因此可以建立一個\(vis\) 陣列和\(Find\) 分別標記這個數是否是要找的數,以及這個要找的數已經在區間內的次數。
程式碼
int n, m; int vis[100005]; int Find[100005]; int a[100005]; int main() { while (scanf("%d%d", &n, &m), n && m) { for (int i = 0; i < n; i++) a[i] = readint(); while (m--) { for (int i = 0; i < n; i++) vis[i] = 0, Find[i] = 0; int q = readint(); int cnt = 0; int ans = n; int x; for (int i = 0; i < q; i++) { x = readint(); if (!vis[x]) cnt++; vis[x]++; } q = cnt; for (int i = 0; i < q; i++) if (vis[a[i]] && !Find[a[i]]) cnt--,Find[a[i]]++; else if (vis[a[i]]) Find[a[i]]++; if (!cnt) ans = q; int l = 0, r = q ; for (int l = 0; l + q <= n; l++) { while (cnt && r < n) { if (vis[a[r++]] && !Find[a[r - 1]]) cnt--,Find[a[r - 1]]++; else if (vis[a[r - 1]]) Find[a[r - 1]]++; } if (!cnt) ans = min(ans, r - l); if (Find[a[l]]) { Find[a[l]]--; if (!Find[a[l]]) cnt++; } } printf("%d\n", ans); } } }