题目:
每门课的作业有截止日期和完成作业所需要的日期, 然后有多门课,如果每门课没完成会有一个惩罚,惩罚为多出来的时间。 求做作业的顺序,使得惩罚最小。
有T组数据, 每组数据 给出N门课,每门课给出了S(课程名) D(截止日期) C(完成作业所需的时间)
思路:
课的总数 <= 15, 设dp[i] 为状态i时候最优解的值, 然后状态i可以通过状态j转移过来,条件是i中至少含有一个j中没有的1。 然后就枚举所有的i中的1, dp[i] = dp[j] + cost[i<-j], 但是这个时候可能需要 惩罚 ,可能不需要惩罚。dp[i] = dp[j] + max(0,last[i] + cost[i<-j] - d[i<-j])。
代码:
#include <cstring>
#include <stdio.h>
#include <iostream>
using namespace std;
int Case;
int n;
const int maxn = 21;
int d[maxn];
int t[maxn];
int pre[1 << 16];
char name[maxn][160];
int dp[1 << 16];
int time[1 << 16];
void print(int state) {
if(state == 0) return ;
print(pre[state]);
state -= pre[state];
for(int i=0;i<n;i++) {
if(state & (1 << i)) {
puts(name[i]);
}
}
}
int main() {
freopen("in.txt","r",stdin);
scanf("%d",&Case);
while(Case--) {
scanf("%d",&n);
for(int i=0;i<n;i++) {
scanf("%s%d%d",name[i],&d[i],&t[i]);
}
memset(dp,0x3f,sizeof(dp));
dp[0] = 0;
memset(time,0,sizeof(time));
memset(pre,0,sizeof(pre));
int upp = 1 << n;
for(int i=1;i < upp;i ++) {
for(int j=0;j<n;j++) {
if((i & (1 << j)) == 0) {
continue;
}
int prestate = i - (1 << j);
time[i] = time[prestate] + t[j];
int cost = max(0,time[prestate]+t[j] - d[j]);
if(dp[prestate] + cost <= dp[i]) {
dp[i] = dp[prestate] + cost;
pre[i] = prestate;
}
}
}
printf("%d\n",dp[upp-1]);
print(upp-1);
}
}