4149:课程大作业,考点:动态规划、记录路径、字典序小的优先输出

原题:http://bailian.openjudge.cn/practice/4149/

描述

小明是北京大学信息科学技术学院三年级本科生。他喜欢参加各式各样的校园社团。这个学期就要结束了,每个课程大作业的截止时间也快到了,可是小明还没有开始做。每一门课程都有一个课程大作业,每个课程大作业都有截止时间。如果提交时间超过截止时间X天,那么他将会被扣掉X分。对于每个大作业,小明要花费一天或者若干天来完成。他不能同时做多个大作业,只有他完成了当前的项目,才可以开始一个新的项目。小明希望你可以帮助他规划出一个最好的办法(完成大作业的顺序)来减少扣分。

输入

输入包含若干测试样例。
输入的第一行是一个正整数T,代表测试样例数目。
对于每组测试样例,第一行为正整数N(1 <= N <= 15)代表课程数目。
接下来N行,每行包含一个字符串S(不多于50个字符)代表课程名称和两个整数D(代表大作业截止时间)和C(完成该大作业需要的时间)。
注意所有的课程在输入中出现的顺序按照字典序排列。

输出

对于每组测试样例,请输出最小的扣分以及相应的课程完成的顺序。
如果最优方案有多个,请输出字典序靠前的方案。

样例输入

2 
3 
Computer 3 3 
English 20 1 
Math 3 2 
3
Computer 3 3 
English 6 3 
Math 6 3

样例输出

2 
Computer 
Math 
English 
3 
Computer 
English 
Math

解法

思路:动态规划

dp[s]:表示已经完成的作业集合为s时达到的最少扣分

初始条件:dp[0]=0

状态转移:dp[s] = min{ dp[s-j] + dominus(j) } 

由于输出和计算的需要,要保存以下几个变量

  1. 达到最小时的j
  2. 此时的天数(要计算扣分)
  3. 前一个状态(因为要输出路径)
  4. 最小的得分

关键:字典序小的优先输出,所以当相同的时候要比较此时路径的字典序。

代码如下:

 1 #define _CRT_SECURE_NO_WARNINGS
 2 #include <iostream>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <vector>
 6 #include <string>
 7 #define INF 1<<30
 8 using namespace std;
 9 class project {
10 public:
11     string name;
12     int ddl;
13     int need;
14     friend istream& operator>>(istream&in, project&A) {
15         in >> A.name >> A.ddl >> A.need;
16         return in;
17     }
18 }projects[16];
19 class Node {
20 public:
21     int pre;
22     int minscore;
23     int last;
24     int finishday;
25 }dp[(1<<16)+10];//dp[s]表示已经完成的作业集合为s时所能达到的最少扣分
26 int dominus(int nowday, int j) {
27     if (nowday + projects[j].need <= projects[j].ddl)
28         return 0;
29     else
30         return nowday + projects[j].need - projects[j].ddl;
31 }
32 vector<string> getpath(int k) {
33     vector<string>result;
34     while (dp[k].pre != 0) {
35         result.push_back(projects[dp[k].last].name);
36         k = dp[k].pre;
37     }
38     result.push_back(projects[dp[k].last].name);
39     reverse(result.begin(), result.end());
40     return result;
41 }
42 bool operator < (const vector<string>&A, const vector<string>&B) {
43     for (int i = 0, j = 0; i < A.size() && j < B.size(); i++, j++) {
44         if (A[i] < B[j])
45             return true;
46         else if (A[i] > B[j])
47             return false;
48     }
49     return false;
50 }
51 int main()
52 {
53     int T;
54     cin >> T;
55     while (T--) {
56         memset(dp, 0, sizeof(dp));
57         //memset(projects, 0, sizeof(projects));
58         int N;
59         cin >> N;
60         for (int i = 0; i < N; i++)
61             cin >> projects[i];
62         for (int s = 1; s < (1 << N); s++) {
63             dp[s].minscore = INF;
64             for (int j = 0; j < N; j++) {
65                 if ((s >> j) & 1) {
66                     int s_j = s - (1 << j);
67                     int nowscore = dp[s_j].minscore + dominus(dp[s_j].finishday, j);
68                     if ( nowscore< dp[s].minscore) {
69                         dp[s].minscore = nowscore;
70                         dp[s].pre = s_j;
71                         dp[s].last = j;
72                         dp[s].finishday = dp[s_j].finishday + projects[j].need;
73                     }
74                     else if (nowscore == dp[s].minscore) {
75                         vector<string>a = getpath(s_j);
76                         vector<string>b = getpath(dp[s].pre);
77                         if (a < b) {
78                             dp[s].minscore = nowscore;
79                             dp[s].pre = s_j;
80                             dp[s].last = j;
81                             dp[s].finishday = dp[s_j].finishday + projects[j].need;
82                         }
83                     }
84                 }
85             }
86         }
87         cout << dp[(1 << N) - 1].minscore << endl;
88         vector<string>paths = getpath((1 << N) - 1);
89         for (int i = 0; i < paths.size(); i++)
90             cout << paths[i] << endl;
91     }
92 }

 

posted @ 2021-07-10 14:21  永远是个小孩子  阅读(252)  评论(0编辑  收藏  举报