有n个作业,做一门作业会花费一定的时间,每门作业有一个期限,过了期限每天就会扣分,问该怎样安排写作业的顺序,使得扣分最小。
状态压缩dp
dp[i] 表示 完成i所对应的集合的作业的最小花费,一开始初始化为INF, dp[i] = min(dp[i-{j} + cost),其中cost表示完成了i包含的作业后完成j的花费。
由于要输出路径,所以要记录每个状态的上一个状态,最后反向打印
#include <cstdio>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
int t,n,d[16],c[16],dp[1<<15],pre[1<<15];
char s[16][110];
void print(int stat){
if(!stat) return ;
int t = 0 ;
for(int i = 0;i<n;i++)
if((stat & (1<<i)) != 0 && (pre[stat] & (1<<i)) == 0){
t = i;
break;
}
print(pre[stat]);
printf("%s\n",s[t]);
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i = 0;i<n;i++)
scanf("%s%d%d",s[i],d+i,c+i);
for(int i = 0;i<(1<<15);i++)
dp[i] = INF;
dp[0] = 0;
for(int i = 0;i<(1<<n);i++){
for(int j = 0;j<n;j++){
if(i & (1<<j)) continue;
int cost = 0;
for(int k = 0;k<n;k++)
if(i & (1<<k)) cost += c[k];
cost += c[j];
cost = max(0,cost - d[j]);
if(dp[i | (1<<j)] > dp[i] + cost){
dp[i | (1<<j)] = dp[i] + cost;
pre[i | (1<<j)] = i;
}
}
}
printf("%d\n",dp[(1<<n)-1]);
print((1<<n)-1);
}
return 0;
}