题意:
有n门课程的作业,每门作业都有截止时间和做作业花费时间,当到截至时间该作业还没做完时会扣除延期时间数量的分数,问按怎样的顺序来做扣除的总分数最短,输出最短时间,完成作业的顺序(按字典序输出)。
思路:
状态压缩作业的完成情况,二进制位上的1表示对应作业完成,共有(1 << n)- 1种作业状态,从1到 (1 << n)- 1dp过去,状态转移式为:
p[i].dp = min( p[i].dp, p[i ^ (1 << j)].dp + min(0, start[j] - p[i ^ (1 << j)].time - cost[j]) )。
p[i].dp表示状态i的总扣分,start[j]表示课程j的截至时间,cost[j]表示完成课程j要花费的时间。
code:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<stack>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 1e8 + 5;
struct node{
int time, dp, pre, ind; //完成目前状态作业花的时间, 扣分, 记录最优转移前继节点,当前作业
} p[maxn];
void init(int n){
for(int i = 0; i <= n; i++){
p[i].dp = inf;
}
}
int main(){
int T, n, start[20], cost[20];
char s[20][105];
scanf("%d", &T);
while(T--){
scanf("%d", &n);
init(1 << n);
for(int i = 0; i < n; i++)
scanf("%s%d%d", s[i], &start[i], &cost[i]);
p[0].dp = p[0].time = 0; //处理边界值
for(int i = 1; i < 1 << n; i++){
for(int j = n - 1; j >= 0; j--){
if(i & (1 << j)){
int num = i ^ (1 << j);
int sum = max(0, p[num].time + cost[j] - start[j]);
if(p[i].dp > p[num].dp + sum){ //状态转移
p[i].dp = p[num].dp + sum;
p[i].time = p[num].time + cost[j];
p[i].ind = j;
p[i].pre = num;
}
}
}
}
stack<int> st;
while(st.size()) st.pop();
int x = (1 << n) - 1;
while(x != 0){
st.push(p[x].ind);
x = p[x].pre;
}
printf("%d\n", p[(1 << n) - 1].dp);
while(st.size()){
x = st.top();
st.pop();
printf("%s\n", s[x]);
}
}
}