題目:
每門課的作業有截止日期和完成作業所需要的日期, 然後有多門課,如果每門課沒完成會有一個懲罰,懲罰爲多出來的時間。 求做作業的順序,使得懲罰最小。
有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);
}
}