题目大意:有N个功课,给出每个功课的截止时间和做完这个功课所需要的时间
如果到了截止时间,该功课还没有做完,就要付出 做完该功课的时间-该功课的截止时间 的代价
问做完所有功课所需花费的最小代价,和做功课的顺序
解题思路:因为只有15门功课,所以可以使用状态压缩
预处理出每种状态下完成功课的时间
用dp[i]表示做完i状态下的功课所需要付出的最小代价
假设i状态下,j功课还没有做
dp[i | (1 << j)] = min(dp[i | (1 << j)], dp[i] + Time[i | (1 << j)] - deadline[j])
其中Time指的是完成该状态下的功课所需要花费的时间,deadline表示截止时间
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>
using namespace std;
const int N = (1 << 15) + 10;
const int INF = (1 << 30);
struct Course{
char name[110];
int deadline, last;
}C[20];
int n;
int dp[N], path[N];
int Time[N], pow2[30];
void init() {
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%s%d%d", C[i].name, &C[i].deadline, &C[i].last);
for (int i = 0; i < (1 << n); i++) {
Time[i] = 0;
dp[i] = INF;
for (int j = 0; j < n; j++)
if (i & (1 << j)) Time[i] += C[j].last;
}
}
void out(int s) {
if (s == 0)
return ;
out(path[s]);
int t = s - path[s];
for (int i = 0; i < n; i++)
if (pow2[i] == t) {
printf("%s\n", C[i].name);
return ;
}
}
void solve() {
queue<int> Q;
for (int i = 0; i < n; i++) {
Q.push(1 << i);
int t = C[i].last;
int Sum = (C[i].last > C[i].deadline ? C[i].last - C[i].deadline : 0);
dp[1 << i] = Sum;
path[1 << i] = 0;
}
while (!Q.empty()) {
int s = Q.front(); Q.pop();
for (int i = 0; i < n; i++) {
if (s & (1 << i)) continue;
else {
int t = Time[s | (1 << i)];
int Sum = (t > C[i].deadline ? t - C[i].deadline : 0);
if (dp[s | (1 << i)] > dp[s] + Sum) {
dp[s | (1 << i)] = dp[s] + Sum;
path[s | (1 << i)] = s;
Q.push(s | (1 << i));
}
}
}
}
printf("%d\n", dp[(1 << n) - 1]);
out((1 << n) - 1);
}
void start() {
pow2[0] = 1;
for (int i = 1 ; i < 17; i++)
pow2[i] = pow2[i - 1] * 2;
}
int main() {
start();
int test;
scanf("%d", &test);
while (test--) {
init();
solve();
}
return 0;
}