題意:
一共 個人,每個人有兩個屬性,、,表示收買此人的兩種方式,一種方式是花 的錢,另一種方式是已經收買了 個人,現詢問收買所有人的最小花費。
思路:
比賽時看到了此題,第一思路是貪心。當時的貪心策略是先把一定要付錢的人都找出來,即對於第 層來說,,即第 層的人需要用錢收買,邊收買邊判斷 是否成立,成立則繼續收買。然後再將第 層的人直接算到所有人集合中,接下來每次選取花費最少的人收買,並且收買完後看是否有一層的人可以被集體收買。
這樣貪心不對。主要原因在於每次選取花費最少的人不一定最優,因爲可能選取花費次小的但是必定需要收買的人,然後花費最少的人所在的層就直接被集體收買了。因此最大的問題就在於沒有處理好在貪心的過程中,有一些人變成了必須花錢才能收買,而這種策略沒有收買。
因此我們需要換一種策略,每次選人將所有必須花錢的人找出來,並且在這些人中選取花費最小的。
我們可以將所有人按 分層,再倒着枚舉層數,枚舉到第 層時,就將第 層的人加入小根堆中,將所有必須付錢的人從堆中彈出。
於是問題就變成了枚舉到第 層時,堆中最多有多少人。不難發現答案爲 人,因爲想要集體收買該層需要 人,所以隊列中最多有 人,至此即可完成此題。
總結:
貪心問題,最忌諱的就是有一個不成熟的想法就馬上去實現,然後實現了半天之後還 了… 這會對比賽結果產生很大的負面影響。
因此貪心問題,考慮到一個策略之後,一定要反覆 自己,並且要嘗試去證明該策略的正確。這一過程其實花不了太多的時間,最多 左右就可以發現問題,而且有利於進一步完善思路。因此想要策略之後一定要再多想想,切忌盲目切題!
代碼:
#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a);
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define per(i,a,b) for(int i = a; i >= b; i--)
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
typedef long long ll;
typedef double db;
const int N = 2e5+100;
const db EPS = 1e-9;
using namespace std;
void dbg() {cout << "\n";}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}
int n,m,p;
vector<int> v[N];
priority_queue<int> q;
int main()
{
int _; scanf("%d",&_);
while(_--){
scanf("%d",&n);
rep(i,0,n) v[i].clear();
rep(i,1,n) scanf("%d%d",&m,&p), v[m].push_back(p);
while(q.size()) q.pop();
ll ans = 0;
per(i,n,0){
for(auto& tp:v[i]) q.push(-tp);
while(q.size() > n-i) ans -= q.top(), q.pop();
}
printf("%lld\n",ans);
}
return 0;
}