看一下中文版的题目就好,英文题目太晦涩了。
有两种方法可以解题
一种是贪心+优先队列
另一种是贪心+并查集
优先队列 需要说的都在代码注释里
#include<cstdio> #include<queue> #include<algorithm> using namespace std; struct s{ int day, val; }arr[100005]; bool cmp(s a, s b){ if(a.day != b.day) return a.day > b.day; return a.val > b.val; } int main(){ int n; while(scanf("%d",&n)!=EOF){ if(n==0){puts("0");continue;} priority_queue<int>Q; for(int i=0;i<n;i++) scanf("%d%d",&arr[i].val,&arr[i].day); sort(arr,arr+n,cmp); /// 注意 这里的排序是将日期大的放在了前面 int cnt = arr[0].day,sum=0; /// 这里是暴力模拟每一天 /// 假设从小到大模拟 /// 第一天的物品价值较低 /// 而第二天有两个物品的价值都较高 /// 那么可以将第二天的一个物品安排到第一天 /// 会发生冲突 /// 而从大到小模拟的话 /// 因为已经将日期在后面的安排完了 /// 所以不会发生从后面来的冲突 for(int i=0;cnt;cnt--){ for(;i<n && cnt <= arr[i].day;i++){ Q.push(arr[i].val); } if(!Q.empty()) sum += Q.top(),Q.pop(); } printf("%d ",sum); } return 0; }
并查集
并查集所结合的贪心策略与优先队列不同
是将物品按价值大小排序
然后将价值最大的物品放在它的截止日期上(因为是第一件物品所以整个日期区间没有安放任何物品)
再将第二件物品放在对应的截止日期上。
这时问题来了,两件物品同一个截止日期怎么办?
因为是价值大的优先安放,所以应该保大舍小(其实并不一定会舍弃)
将当前的这个 “小” 安放在其他的可行日期
从大到小遍历[1,截止日期]这个区间
寻找没有被占用的日期
这时又有了新的问题,遍历的复杂度过高。
可以用并查集来优化。
并查集记录的父节点是当前日期之前的第一个没被占用的日期
路径压缩可以降低复杂度。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct s{ int d, p; }arr[10010]; int pre[10010]; bool cmp(s a,s b){ return a.p > b.p; } int find_(int x){ if(pre[x] == -1) return x; return pre[x] = find_(pre[x]); } int main(){ int n; while(scanf("%d",&n)!=EOF){ memset(pre,-1,sizeof(pre)); for(int i=0;i<n;i++) scanf("%d%d",&arr[i].p,&arr[i].d); sort(arr,arr+n,cmp); int ans = 0; for(int i=0;i<n;i++){ int t = find_(arr[i].d); if(t>0){ ans += arr[i].p; pre[t] = t-1; } } printf("%d ",ans); } return 0; }