小编:真是一道有趣的题,通过量还不算低,我也就写写自己的想法
题目描述
老师在开学第一天就把所有作业都布置了,每个作业如果在规定的时间内交上来的话才有学分。每个作业的截止日期和学分可能是不同的。例如如果一个作业学分为10分,要求在6天内交,那么要想拿到这10学分,就必须在第6天结束前交。每个作业的完成时间都是只有一天。例如,假设有7次作业的学分和完成时间如下:
作业号 | 期限 | 学分 |
---|---|---|
1 | 1 | 6 |
2 | 1 | 7 |
3 | 3 | 2 |
4 | 3 | 1 |
6 | 2 | 5 |
7 | 6 | 1 |
最多可以获得15学分,其中一个完成作业的次序为2,6,3,1,7,5,4,注意可能还有其他方法。你的任务就是找到一个完成作业的顺序获得最大学分。
- 输入格式第一行一个整数N,表示作业的数量;接下来N行,每行包括两个整数,第一个整数表示作业的完成期限,第二个数表示该作业的学分。* 输出格式输出一个整数表示可以获得的最大学分。保证答案不超过 C/C++ 的 int 范围。
样例输入
7
1 6
1 7
3 2
3 1
2 4
2 5
6 1
样例输出
15
数据范围与提示
100% N <= 1000000;
分析
很明显这是一道贪心题,先做分值高的。实现起来就是:先按分值大小排序,再枚举空出来的时间,选择最靠后的,如果还有空出来的时间就说明这个作业能完成,累加最终的答案,这样一定能得到最优的解
STEP1
我尝试了一下暴力,这也是最容易想到的,最后以超时告终,拿到80分
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 1000005;
struct node {
int t, v;
} a[MAXN];
bool flag[MAXN];
bool cmp (node x, node y) { // 排序 cmp 函数
if(x.v == y.v)
return x.t < y.t;
else return x.v > y.v;
}
int main() {
int n, ans = 0;
scanf ("%d", &n);
for(int i = 1; i <= n; i++) scanf ("%d %d", &a[i].t, &a[i].v);
sort(a + 1, a + n + 1, cmp);
for(int i = 1; i <= n; i++) {
for(int j = a[i].t; j >= 1; j--) { // 倒着向前枚举
if(flag[j] == false) {
flag[j] = true; // 更新此时间为被占用
ans += a[i].v; // 累加答案
break; // 找到了就跳出
}
}
}
printf("%d", ans);
return 0;
}
STEP2
并查集优化即可
模板:
void void Make_Set(int n) { // 初始化
for(int i = 1; i <= n; i++)
fa[i] = i; // 指向自己
return ;
}
int Find_Set(int x) { // 查询根结点 递归实现
if(fa[x] != x) return fa[x] = Find_Set(fa[x]);
else return fa[x];
}
void Union_Set(int x, int y) { // 合并
int u = Find_Set(x);
int v = Find_Set(y);
if(u != v) fa[v] = u;
return ;
}
AC代码
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 1000005;
struct node {
int t, v;
} a[MAXN];
bool flag;
int fa[MAXN];
bool cmp (node x, node y) { // 排序 cmp 函数
if(x.v == y.v)
return x.t < y.t;
else return x.v > y.v;
}
void Make_Set(int n) { // 初始化
for(int i = 1; i <= n; i++) fa[i] = i; // 指向自己
return ;
}
int Find_Set(int x) { // 查找空闲时间
if(x <= 0) return x;
else if(fa[x] == x) { // 空闲时间就是上一个
flag = true;
fa[x] = x - 1;
return fa[x];
}
else return fa[x] = Find_Set(fa[x]); // 递归
}
int main() {
int n, ans = 0;
scanf ("%d", &n);
Make_Set(n);
for(int i = 1; i <= n; i++) scanf ("%d %d", &a[i].t, &a[i].v);
sort(a + 1, a + n + 1, cmp);
for(int i = 1; i <= n; i++) {
flag = false; // 找到空闲时间则为 true ,相反为 false
fa[a[i].t] = Find_Set(a[i].t); // a[i].t 的父亲结点置为之前最近的空闲时间
if(flag) ans += a[i].v; // 累加答案
}
printf("%d", ans);
return 0;
}