题意
ZJM 有 n 个作业,每个作业都有自己的 DDL,如果 ZJM 没有在 DDL 前做完这个作业,那么老师会扣掉这个作业的全部平时分
所以 ZJM 想知道如何安排做作业的顺序,才能尽可能少扣一点分。
请你帮帮他吧!
Input
输入包含T个测试用例。输入的第一行是单个整数T,为测试用例的数量。
每个测试用例以一个正整数N开头(1<=N<=1000),表示作业的数量。
然后两行。第一行包含N个整数,表示DDL,下一行包含N个整数,表示扣的分。
Output
对于每个测试用例,您应该输出最小的总降低分数,每个测试用例一行。
Sample Input
3
3
3 3 3
10 5 1
3
1 3 1
6 2 3
7
1 4 6 4 2 4 3
3 2 1 7 6 5 4
Sample Output
0
3
5
Hint
上方有三组样例。
对于第一组样例,有三个作业它们的DDL均为第三天,ZJM每天做一个正好在DDL前全部做完,所以没有扣分,输出0。
对于第二组样例,有三个作业,它们的DDL分别为第一天,第三天、第一天。ZJM在第一天做了第一个作业,第二天做了第二个作业,共扣了3分,输出3。
思路
从最后一个DDL开始,向前走,每向前走一天就加上当天为ddl的所有作业,然后解决一个分值最大的作业,最后剩下的都是相对小分值得作业,这样才能导致最后剩下的作业里面扣的分最少。
代码
#include <iostream>
#include <cstdio>
#include <unordered_map>
#include <queue>
#include <algorithm>
using namespace std;
unordered_map<int, int> mp;
struct m
{//存储ddl 和 分数
int ddl;
int score;
bool operator < (const m& b) const
{//排序按 扣的分排 最大的优先
if (score != b.score) return score < b.score;
return ddl > b.ddl;
}
};
vector<m> a(5000);
bool cmp(const m& a, const m& b)
{//先按时间排序 最大ddl排到前面
if (a.ddl != b.ddl) return a.ddl > b.ddl;
return a.score > b.score;
}
int main(int argc, char** argv)
{
int T;
scanf("%d", &T);
int n;
for (int t = 0; t < T; t++)
{
priority_queue<m> b;//优先队列 按分值大小排序
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &a[i].ddl);
mp[a[i].ddl]++; //记录每个ddl下有多少个作业
}
for (int i = 0; i < n; i++)
{
scanf("%d", &a[i].score);
}
sort(a.begin(), a.begin()+n, cmp); //按ddl排序
int alen = 0;
for (int i = a[0].ddl; i > 0; i--)
{//i从最晚得一个ddl开始 向前走
if (mp[i]!=0)
{//将以当天为ddl得所有作业加入优先队列
for (int j = 0; j < mp[i]; j++)
{
b.push(a[alen]);
alen++;
}
}
if (!b.empty())
{
b.pop(); //队列中有作业得话 就每天删除一个最大得
}
}
int sum = 0;
while(!b.empty())
{
sum+=b.top().score; //计算剩下的没做完得作业分数
b.pop();
}
cout << sum << endl;
mp.clear();
}
return 0;
}
反思及总结
这道题考验贪心算法;
刚开始没有想到用优先队列来做堆,使用了STL里面的make_heap方法,底层为vector容器实现,发现一个小问题,就是c++ STL里面的堆实现删除并不是真正的删除了堆顶元素,而是将元素放在了vector的尾部,导致每次都做同一份作业。后面改用优先队列存储,同样的思想,一边过。