Doing Homework again(贪心 + 思维)

Doing Homework again(贪心 + 思维)

题目描述:

Ignatius has just come back school from the 30th ACM/ICPC. Now he has a lot of homework to do. Every teacher gives him a deadline of handing in the homework. If Ignatius hands in the homework after the deadline, the teacher will reduce his score of the final test. And now we assume that doing everyone homework always takes one day. So Ignatius wants you to help him to arrange the order of doing homework to minimize the reduced score.

大意: 每天能补一种作业,超过ddl未补的作业会扣分,求扣分最少值。

Input
The input contains several test cases. The first line of the input is a single integer T that is the number of test cases. T test cases follow.
Each test case start with a positive integer N(1<=N<=1000) which indicate the number of homework… Then 2 lines follow. The first line contains N integers that indicate the deadlines of the subjects, and the next line contains N integers that indicate the reduced scores.

Output
For each test case, you should output the smallest total reduced score, one line per test case.

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

思路

一开始想用 pair + 优先队列,先按 ddl 顺序排序,ddl 相同则按扣分值由大到小排序,然后将队列中元素遍历,将同一天内最大扣分值的元素弹出,剩下的记录下来累计扣分值。但是这种算法有一个漏洞,如以下几个数据:

1 2 2
1 100 150

此时若按照这个算法进行计算,算出来的最小扣分值为 100(第一天补第一天的作业,之后第二天补扣分值为 150 的作业,最后剩下第二天扣分值为 100 的作业没写,扣分 100)。然而,我们可以第一天补扣分值为 100 的ddl为第二天的作业,第二天补扣分值为 150 的ddl为第二天的作业,此时最小扣分值为 1,优于 100。

因此,将ddl作为第一关键字排序时不可行的。

我们可以尝试将扣分值作为第一关键字进行排序,将扣分值更大的优先做完。那么下一个问题,我们要安排在哪一天完成这些作业呢?

假如优先安排在前面,则有可能出现以下数据:

2 1
200 100

此时,假如我们优先向前面安排,则会出现:
1)第一天补 ddl 为第二天的作业
2)第二天时第一天作业超过 ddl,扣分

然而,我们完全可以在第一天补第一天的作业,在第二天补第二天的作业。因此,我们应该优先安排在更靠后的天数。

即,从 ddl 这一天开始,能安排则安排,不能安排则向前推一天,如此往复。若最后仍然无法安排到,则说明前面的天数已经被扣分值更大的选项给安排满了,那么此时就累计当前扣分值。

AC代码:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;

struct H {
	int ddl, s;
	bool operator <(const H& a) {
		return s == a.s ? (ddl < a.ddl) : (s > a.s);//以扣分值大小为关键字进行排序
	}
}h[1005];

bool vis[1005] = { 0 };//记录当前这一天是否已经被安排

int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		memset(vis, 0, sizeof(vis));
		memset(h, 0, sizeof(h));
		int n;
		scanf("%d", &n);
		for (int i = 1;i <= n;i++)
			scanf("%d", &h[i].ddl);
		for (int i = 1;i <= n;i++)
			scanf("%d", &h[i].s);
		sort(h + 1, h + 1 + n);
		int sum = 0;
		for (int i = 1;i <= n;i++)
		{
			if (vis[h[i].ddl] == 0)
				vis[h[i].ddl] = 1;
			else
			{
				bool flag = 0;//记录是否找到无安排的一天
				for (int j = h[i].ddl;j >= 1;j--)//注意这里 j 要从 ddl 这一天开始,依次向前遍历,理由之前已经写过
				{
					if (vis[j] == 0)//若找到的话就安排在这一天
					{
						vis[j] = 1;
						flag = 1;
						break;
					}
				}
				if (!flag) sum += h[i].s;//若找不到能安排的一天,就累计扣分值
			}
		}
		printf("%d\n", sum);
	}
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值