題解
題目連結
P3045 [USACO12FEB] Cow Coupons G
解題思路
反悔貪心
大體思路即為: 將所有 \(p_i\) 與 \(c_i\) 分別存入兩個小根堆中,每次取出下標未使用過的堆頂元素 \(p_i\) 與 \(c_j\),並變成實際的應增加費用 \(p_i\) 與 \(c_j + \Delta k\) (最小的 \(p_k - c_k\)) 進行比較,決定選擇 \(i\) 還是 \(j\) . 具體的思路與證明可以看Luogu題解, 裡面非常詳細與全面.
重點程式碼部分就是
if (tc + delta.top() > tp) { //相當於將 對i使用優惠卷實際上應該增加的費用 和 不對j使用優惠卷應該增加的費用 作比較
m -= tp;
P.pop();
vis[i] = 1;
}
else {
m -= tc + delta.top();
delta.pop();
C.pop();
vis[j] = 1;
delta.push(p[j] - c[j]);
}
這裡Luogu的大部分題解寫的都是 移項後的 \(delta.top() > tp - tc\), 困擾了我非常久:為什麼tp和tc明明對應的下標不一定一樣,卻可以直接相減.
時間複雜度與空間複雜度分析
由於堆的使用, 兩個最大大小為n和一個大小為k的堆,時間與空間複雜度均為 \(O(nlogn)\) 左右.
完整程式碼
#include <bits/stdc++.h>
#define int long long
using namespace std;
using PII = pair<int, int>;
signed main() {
ios::sync_with_stdio(0), cin.tie(0);
int n, k, m;
cin >> n >> k >> m;
priority_queue<PII, vector<PII>, greater<>> P, C;
priority_queue<int, vector<int>, greater<>> delta;
vector<int> p(n + 1), c(n + 1), vis(n + 1);
for (int i = 1; i <= n; ++i) {
cin >> p[i] >> c[i];
P.push({p[i], i});
C.push({c[i], i});
}
for (int i = 1; i <= k; ++i) delta.push(0); //相當於給了k次無腦使用優惠卷的機會
int ans = 0;
while (P.size()) {
auto [tp, i] = P.top();
auto [tc, j] = C.top();
if (vis[i]) {
P.pop();
continue;
}
if (vis[j]) {
C.pop();
continue;
}
if (tc + delta.top() > tp) { //相當於將 對i使用優惠卷實際上應該增加的費用 和 不對j使用優惠卷應該增加的費用 作比較
m -= tp;
P.pop();
vis[i] = 1;
}
else {
m -= tc + delta.top();
delta.pop();
C.pop();
vis[j] = 1;
delta.push(p[j] - c[j]);
}
if (m >= 0) ans++;
else break;
}
cout << ans << "\n";
return 0;
}
AC提交記錄
(https://www.luogu.com.cn/record/176544896)