问题描述:
某学科老师布置了n个题目,每个题目都有相应的分数及截止日期。各个题目的分数及截止日期可能并不相同。对某题目而言,如果在该题目的截止日期前完成则可获得对应的分数,否则无法得分。假设每个题目均需要花费一天的时间来完成,这期间无法完成其他题目。请你设计算法指定题目的完成计划,从而使总的得分最大。 下面给出一个包含了7个题目及相应的分数、截止日期的实例:
题目 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
分数 |
6 |
7 |
2 |
1 |
4 |
5 |
1 |
截止日期(天) |
1 |
1 |
3 |
3 |
2 |
2 |
4 |
对该实例而言,得分最大的作业完成方案为花费4天时间依次完成题目2,6,3,7。得分为15。
【输入形式】
输入数据第一行为一个整数n (0 <= n <= 10000), 表示题目数目 之后n行各有两个整数, 第i行为 pi, di (1 <= pi, di <= 10000),分别表示第i个题目的分数和截止时间
【输出形式】
一个整数, 为当前条件下的最大得分
【样例输入】
4
50 2
10 1
20 2
30 1
【样例输出】
80
分析
本题适合用贪心算法来求解,先按照任务价值从大到小排序,然后依次取出后对任务进行判断。如果取出任务截至当天未被占用,则将该任务价值加入总价值后将当天标记为占用。如果之前判断的时间被占用,则往前推算一天后再进行判断。
伪代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 对于i = 0 ~ n{ 如果任务i截止日期当天没有被占用{ 结果加上第i个任务的分数 任务i截止日期当天标记为已被占用; } 如果任务截止日期当天被占用{ 对于j = 任务i截止日期-1 ~ 0){ 如果第j天被占用{ 结果加上第i个任务的分数 第j天标记为已被占用 跳出循环 } } }
|
代码(JAVA1)
deadline类:
1 2 3 4 5 6 7 8 9 10 11 12
| public class deadline { int score; int day; public deadline(int score,int day) { this.score = score; this.day = day; } public int getScore() { return score; } }
|
test类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| import java.util.Arrays; import java.util.Comparator; import java.util.Scanner;
public class test { public static void Sort(deadline[] overdays) { Arrays.sort(overdays, new Comparator < deadline > () { public int compare(deadline o1, deadline o2) { return (int)(o2.getScore() - o1.score); } }); } public static void main(String args[]) { int n, a, b, number = 0, count = 0; Scanner input = new Scanner(System.in); n = input.nextInt(); if (n == 0) { System.out.println("0");
} deadline[] overdays = new deadline[n]; for (int i = 0; i < n; i++) { overdays[i] = new deadline(input.nextInt(), input.nextInt()); } input.close(); Sort(overdays); int max = overdays[0].day; for (int i = 1; i < n; i++) { if (max < overdays[i].day) max = overdays[i].day; } int[] days = new int[max + 1]; for (int i = 0; i <= max; i++) days[i] = 1; for (int i = 0; i < n; i++) { if (days[overdays[i].day] == 1) { number += overdays[i].score; days[overdays[i].day] = 0; } else { for (int j = overdays[i].day - 1; j > 0; j--) { if (days[j] == 1) { number += overdays[i].score; days[j] = 0; break; } } } } System.out.println(number); } }
|
代码(JAVA2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| import java.util.Scanner;
public class deadline { public static int maxinarr(int[] a, int len) { int max = a[0]; for (int i = 0; i < len; i++) { if (max < a[i]) max = a[i]; } return max; } public static void swaparr(int a[], int b[], int len) { int k; for (int i = 0; i < len; i++) { for (int j = 0; j < len - 1; j++) { if (a[j] < a[j + 1]) { k = a[j]; a[j] = a[j + 1]; a[j + 1] = k; k = b[j]; b[j] = b[j + 1]; b[j + 1] = k; } } } } public static boolean judge(int day[], int data[], int n) { boolean ret = false; for (int i = data[n] - 1; i >= 0; i--) { if (day[i] == 0) { day[i] = 1; ret = true; break; } } return ret; } public static void main(String[] args) { int n; Scanner input = new Scanner(System.in); n = input.nextInt(); int[] score = new int[n]; int[] data = new int[n]; for (int i = 0; i < n; i++) { score[i] = input.nextInt(); data[i] = input.nextInt(); } input.close(); swaparr(score, data, n); int a = maxinarr(data, n); int allscore = 0; int[] day = new int[a]; for (int i = 0; i < a; i++) { day[i] = 0; }
for (int i = 0; i < n; i++) { if (judge(day, data, i)) { allscore += score[i]; a--; if (a == -1) break; } } System.out.println(allscore); } }
|
代码(C++1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| #include<iostream> using namespace std;
struct sdata{ int score; int deadline; };
void arrSort(sdata* pData, int length){ sdata temp; for (int i = 0; i < length; i++){ for (int j = 0; j < length-1; j++){ if (pData[j].score < pData[j+1].score){ temp = pData[j]; pData[j] = pData[j+1]; pData[j+1] = temp; } } } }
int main(){ int n,a,b; int answer = 0; int x = 0;
cin >> n;
if(n==0){ cout << "0"; return 0; } sdata *Data = new sdata[n]; for (int i = 0; i < n; i++){ cin >> a; cin >> b;
if(a*b==0){ x++; continue; } Data[i].score=a; Data[i].deadline=b; } n -= x;
arrSort(Data, n);
int max=Data[0].deadline; for (int i = 1; i < n; i++){ if(max<Data[i].deadline) max=Data[i].deadline; }
int schedule[max+1]; for (int i = 0; i <= max; i++) schedule[i]=1;
for (int i = 0; i < n; i++){
if(schedule[Data[i].deadline]==1){ answer += Data[i].score; schedule[Data[i].deadline]=0; }
else{ for(int j=Data[i].deadline-1;j>0;j--){ if(schedule[j]==1){ answer+=Data[i].score; schedule[j]=0; break; } } }
} cout << answer; return 0; }
|
代码(C++2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| #include <iostream>
using namespace std;
void swaparr(int a[], int b[], int len){ for(int i=0;i<len;i++){ for(int j=0;j<len-1;j++){ if(a[j]<a[j+1]){ swap(a[j],a[j+1]); swap(b[j],b[j+1]); } } } }
int maxinarr(int a[],int len){ int max = a[0]; for(int i=0;i<len;i++){ if(max<a[i]) max = a[i]; } return max; }
bool judge(int day[], int data[], int n){ bool ret=0; for(int i=data[n]-1;i>=0;i--){ if(day[i]==0){ day[i]=1; ret = 1; break; } } return ret; }
int main(){ int n; cin >> n; int score[n]; int data[n]; for(int i=0;i<n;i++){ cin >> score[i]; cin >> data[i]; } swaparr(score, data, n); int a=maxinarr(data,n); int allscore=0; int day[a]; for(int i=0;i<a;i++){ day[i]=0; }
for(int i=0;i<n;i++){ if(judge(day,data,i)){ allscore += score[i]; a--; if(a==-1) break; } } cout << allscore; return 0; }
|
算法正确性分析
本算法对规定范围下不同的输入数据能够得出满足要求的结构,对于精心选择的典型、苛刻而带有刁难性的输入数据能够得出满足要求的结果,对于一切合法的输入数据都产生满足要求的结果。
对于核心算法,先按照任务价值从大到小排序,然后依次取出后对任务进行判断。如果取出任务截至当天未被占用,则将该任务价值加入总价值后将当天标记为占用。如果取出任务截止当天被占用,则往前推算一天进行判断。
总结
本题采用贪心算法,通过这次学习,我了解到贪心算法需要建立数学模型来描述问题;把求解的问题分成若干个子问题;对每一子问题求解,得到子问题的局部最优解;把子问题的解局部最优解合成原来解问题的一个解。解决问题需要从问题的某个初始解出发,采用循环语句,当可以向求解目标前进一步时,就根据局部最优策略,得到一个部分解,缩小问题的范围或规模,最后将所有部分解综合起来,得到问题的最终解。