题意:
一共 个人,每个人有两个属性, 、 ,表示收买此人的两种方式,一种方式是花 的钱,另一种方式是已经收买了 个人,现询问收买所有人的最小花费。
思路:
比赛时看到了此题,第一思路是贪心。当时的贪心策略是先把一定要付钱的人都找出来,即对于第 层来说, ,即第 层的人需要用钱收买,边收买边判断 是否成立,成立则继续收买。然后再将第 层的人直接算到所有人集合中,接下来每次选取花费最少的人收买,并且收买完后看是否有一层的人可以被集体收买。
这样贪心不对。主要原因在于每次选取花费最少的人不一定最优,因为可能选取花费次小的但是必定需要收买的人,然后花费最少的人所在的层就直接被集体收买了。因此最大的问题就在于没有处理好在贪心的过程中,有一些人变成了必须花钱才能收买,而这种策略没有收买。
因此我们需要换一种策略,每次选人将所有必须花钱的人找出来,并且在这些人中选取花费最小的。
我们可以将所有人按 分层,再倒着枚举层数,枚举到第 层时,就将第 层的人加入小根堆中,将所有必须付钱的人从堆中弹出。
于是问题就变成了枚举到第 层时,堆中最多有多少人。不难发现答案为 人,因为想要集体收买该层需要 人,所以队列中最多有 人,至此即可完成此题。
总结:
贪心问题,最忌讳的就是有一个不成熟的想法就马上去实现,然后实现了半天之后还 了… 这会对比赛结果产生很大的负面影响。
因此贪心问题,考虑到一个策略之后,一定要反复 自己,并且要尝试去证明该策略的正确。这一过程其实花不了太多的时间,最多 左右就可以发现问题,而且有利于进一步完善思路。因此想要策略之后一定要再多想想,切忌盲目切题!
代码:
#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;
}