目录
1.题目再现
题目链接:4.小郑的数学作业 - 蓝桥云课
2.问题理解
-
学期天数:共有 N 天。
-
数学课:共有 M 节数学课,每节课在第 ai天进行,且每节课一定会布置作业。
-
交作业:共有 K 次交作业,每次交作业在第 bi天进行。每次交作业前,必须完成之前所有布置的作业。
-
幸福度:每天有一个幸福度 ci,如果这天不做作业,小郑会获得完整的 ci 幸福度;如果做作业,幸福度为 0。
-
作业完成规则:每天只能完成一份作业,且交作业是在早上,小郑不能在这天补写当天需要上交的作业。
我们的目标是找到一种策略,使得小郑在按时完成所有作业的前提下,整个学期的幸福度之和最大。
3.解题思路
为了最大化幸福度,我们需要在每次交作业前完成所有布置的作业,同时尽量减少做作业的天数,以保留更多的幸福度。
-
作业完成时机:每次交作业前,必须完成之前所有布置的作业。因此,我们需要在每次交作业前,选择一些天数来做作业。
-
选择做作业的天数:为了最大化幸福度,我们应该选择那些幸福度较低的天数来做作业,因为这样可以保留更多的高幸福度天数。
-
优先队列的使用:为了高效地选择幸福度最低的天数来做作业,我们可以使用一个最小堆(优先队列)来维护这些天数。
4.AC代码
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=510;
int n,m,k,c[N],sum,x;
bool a[N],b[N];
int main(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++) cin>>c[i],sum+=c[i];
for(int i=1;i<=m;i++) cin>>x,a[x]=1;
for(int i=1;i<=k;i++) cin>>x,b[x]=1;
priority_queue<int,vector<int>,greater<int>>q;
int l,r;
for(int i=1;i<=n;i++){
if(b[i]){
while(!q.empty()) q.pop();
r=i-1;
for(int j=r;j>=l;j--){
q.push(c[j]);
if(a[j]) sum-=q.top(),q.pop();
}
l=i;
}
}
cout<<sum;
return 0;
}
/*
其实b[i]就像是划分区间后的临界点,
初始区间的最左边l=1,
从遇到需要提交作业,就从他上一个点(r)向前找,
将数放进最小优先队列里面,
然后遇到a[i],就将最小数减去,更新l=i,
就这样不断地从r到l区间找a[i],就能求出来
*/
5.题目总结
5.1算法核心思想
5.1.1 贪心策略
每次交作业前,需要完成所有布置的作业。
为了最大化幸福度,我们应该尽量选择幸福度最低的天数来做作业,因为这样可以保留更多的高幸福度天数。
这种“每次选择局部最优解”的思想就是贪心算法的核心。
5.1.2 优先队列(堆)的作用:
为了高效地找到当前区间内幸福度最低的天数,代码使用了最小堆(优先队列)。
最小堆可以在 O(logn)的时间内找到并移除最小的元素,非常适合用来动态维护当前区间内的最小幸福度。
5.2 时间复杂度分析
初始化:
输入幸福度、布置作业和交作业的天数,时间复杂度为 O(N+M+K)。
遍历天数:
外层循环遍历 N 天,时间复杂度为 O(N)。
内层循环(处理区间 [l, r]
)在最坏情况下会遍历 N 天,时间复杂度为 O(N)。
每次操作优先队列的时间复杂度为 O(logN)。
总时间复杂度:
最坏情况下,总时间复杂度为 O(NlogN)。
5.3空间复杂度分析
优先队列:
优先队列最多存储 N 个元素,空间复杂度为 O(N)。
其他数组:
a[N]
、b[N]
、c[N]
的空间复杂度为 O(N)。
总空间复杂度:O(N)。