集合覆蓋模型例題_在打CodeForces的過程中發現的一個小模型
阿新 • • 發佈:2021-02-07
技術標籤:集合覆蓋模型例題
不久前的Grakn Forces 2020上,我想出了這個方法,我本來以為這個模型不會很常見。然而,今天的CodeForces #679 Div2上,我第二次碰到了可以用這個模型解決的問題,氣人的是,這次我卻在程式碼細節上寫掛了,最終掉分,憤而決定當作模板記錄下來,以免再犯。
問題
有
個有序對 ,對於每個有序對,可以選擇將 加入集合 或將 加入集合 ,試最小化 。(規定 )演算法
首先注意到對於
和 ,如果在 的同時有 ,那麼就可以捨棄 。於是可以將有序對以 為第一關鍵詞、顯然,
應該隨 單調減。如何保證呢?注意到最後一個有序對肯定要保留,所以我們只需要從後往前遍歷,劃掉破壞 單調性的元素即可:當然,在程式設計中,刪除陣列中的元素還是太浪費時間了,我們轉而把無需刪除的有序對加入一個新的陣列。只不過,因為我們是從後往前遍歷,這樣會導致順序顛倒過來。
這時我們發現,如果我們選了某個
,那麼可以無代價地選擇它 下方的所有 ;如果我們選了某個 ,那麼可以無代價地選擇它 上方的所有 。相當於,我們只需要選擇左邊的 開頭和右邊的 結尾,而且它們應該是 相鄰這樣一來,我們一共就只有
種選法了( 為新陣列的長度),把原來的指數級問題降成了線性。程式碼
int minsum(vector<pair<int, int>> &V) { if (V.empty()) return 0; sort(V.begin(), V.end()); vector<pair<int, int>> U{V[V.size() - 1]}; for (int i = V.size() - 2; i >= 0; --i) if (V[i].second > U.back().second) U.push_back(V[i]); int mi = min(U[0].first, U.back().second); for (int i = 0; i < U.size() - 1; ++i) mi = min(mi, U[i].second + U[i + 1].first); return mi; }
例題
CF1408D:通過計算,可求得每個強盜向上躲過探照燈需走
格,向右躲過探照燈需走 格,記為 。現在要讓所有強盜向上、向右各走若干格躲過所有探照燈,也即選若干個 求最大值,選剩餘的 求最大值,將這兩個最大值的和最小化。這完美符合剛剛那個模型 (當然,因為這模型就是從這個題來的-w-)。CF1435C:我們可以得到
組 個數,每組各選擇一個,讓選擇的數的極差最小。可以固定其中一組,對於這組數中的每一個 ,都把其餘各組中最大的 的數和最小的 的數找出來,分別求它們與 的差(如果沒有滿足某個條件的數,記對應的差為INF
)。這相當於是
向左擴充套件和
向右擴充套件。然後套模型。這樣我們算出了選固定組每一個數時的最小極差,求一個最小值即可。