家庭作业 题解

小编:真是一道有趣的题,通过量还不算低,我也就写写自己的想法

题目描述

老师在开学第一天就把所有作业都布置了,每个作业如果在规定的时间内交上来的话才有学分。每个作业的截止日期和学分可能是不同的。例如如果一个作业学分为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;
}
原文地址:https://www.cnblogs.com/Chain-Forward-Star/p/13868049.html