Doing Homework |
---|
题意: 要完成n门课的作业,每门课的作业分别具有一个截至时间和一个完成需要的时间。此外作业超过截止时间提交需要扣相应的分数。求最小扣分,并按最小字典序输出作业完成顺序。
题解: n ≤ 15,一个美丽的范围,对于这样的数据,都可以考虑通过状态压缩来完成。
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
const int inf = 0x3f3f3f3f;
const int maxn = (1 << 15) + 10;
int dp[maxn], t[maxn]; //dp[i]记录到达状态i扣的最少分,t时相应的花去多少天了
int pre[maxn]; //特别的是,只有dp需要进行初始化
int ddl[20], fin[20]; //截止时间、完成时间
char s[20][100];
void print(int x) {
if(x == 0) return;
print(x - (1 << pre[x]));
printf("%s\n", s[pre[x]]);
}
void run() {
int n = read();
for(int i = 0; i < n; i++) scanf("%s%d%d", s[i], &ddl[i], &fin[i]);
int tot = 1 << n; //状态
for(int i = 1; i < tot; i++) {
dp[i] = inf; 初始化到达状态i的扣分
for(int j = n - 1; j >= 0; j--) { //为保证字典序的最小,从后往前遍历
int tem = 1 << j; //单完成作业j的状态
if(!(i & tem)) continue; //状态i中并没有完成作业j
int score = t[i - tem] + fin[j] - ddl[j];
if(score < 0) score = 0; //i-tem表示没有完成j的那个状态
if(dp[i] > dp[i - tem] + score) {
dp[i] = dp[i - tem] + score;
t[i] = t[i - tem] + fin[j];
pre[i] = j; //i对应的是完成某些作业时的状态,而j对应作业的编号
} //状态都是独一无二的,所以某个状态的pre不用特意初始化
}
}
printf("%d\n", dp[tot - 1]);
print(tot - 1);
}
int main() {
int _T = read();
while(_T--) run();
return 0;
}