有n个任务,每个任务有一个截止时间,超过截止时间一天,要扣一个分。
求如何安排任务,使得扣的分数最少。Input有多组测试数据。第一行一个整数表示测试数据的组数
第一行一个整数n(1<=n<=15)
接下来n行,每行一个字符串(长度不超过100)表示任务的名称和两个整数,分别表示任务的截止时间和完成任务需要的天数。 这n个任务是按照字符串的字典序从小到大给出。Output每组测试数据,输出最少扣的分数的。 并输出一个完成任务的方案,如果有多个方案,输出字典序最小的一个。Sample Input
2 3 Computer 3 3 English 20 1 Math 3 2 3 Computer 3 3 English 6 3 Math 6 3
Sample Output
2 Computer Math English 3 Computer English Math
思路:数据范围可以状态压缩,dp[i]表示i状态下的最小扣分,状态转移为dp[i] = min(dp[pre]+dp[pre].time-limit[j]),dp[pre].time表示pre状态下花费的总时间,j表示状态pre转移到i时是经过哪一个中间态,即:pre = i^(1<<j)
const int maxm = (1<<15)+5; int finish[20], limit[20], n; struct Node { int fa, task, ans, time; } dp[maxm]; map<int, string> Cache; void run_case() { Cache.clear(); string str; cin >> n; for(int i = 0; i < n; ++i) { cin >> str >> limit[i] >> finish[i]; Cache[i] = str; } for(int i = 1; i < (1<<n); ++i) { dp[i].ans = 0x3f3f3f3f; for(int j = n-1; j >= 0; --j) { if(i&(1<<j)) { int pre = i^(1<<j); int now = dp[pre].time+finish[j]-limit[j]; if(now < 0) now = 0; if(now+dp[pre].ans<dp[i].ans) { dp[i].fa = pre; dp[i].task = j; dp[i].ans = now+dp[pre].ans; dp[i].time = dp[pre].time + finish[j]; } } } } int End = (1<<n)-1; cout << dp[End].ans << "\n"; vector<int> ans; while(End) { ans.push_back(dp[End].task); End = dp[End].fa; } for(auto i = ans.rbegin(); i != ans.rend(); ++i) { cout << Cache[*i] << "\n"; } return; } int main() { ios::sync_with_stdio(false), cin.tie(0); int T; cin >> T; while(T--) { run_case(); } return 0; }View Code