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) }
由于输出和计算的需要,要保存以下几个变量
- 达到最小时的j
- 此时的天数(要计算扣分)
- 前一个状态(因为要输出路径)
- 最小的得分
关键:字典序小的优先输出,所以当相同的时候要比较此时路径的字典序。
代码如下:
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 }