一、题目描述
Description
马上假期就要结束了,zjm还有 n 个作业,完成某个作业需要一定的时间,而且每个作业有一个截止时间,若超过截止时间,一天就要扣一分。
zjm想知道如何安排做作业,使得扣的分数最少。
Tips: 如果开始做某个作业,就必须把这个作业做完了,才能做下一个作业。
Input
有多组测试数据。第一行一个整数表示测试数据的组数
第一行一个整数 n(1<=n<=15)
接下来n行,每行一个字符串(长度不超过100) S 表示任务的名称和两个整数 D 和 C,分别表示任务的截止时间和完成任务需要的天数。
这 n 个任务是按照字符串的字典序从小到大给出。
Output
每组测试数据,输出最少扣的分数,并输出完成作业的方案,如果有多个方案,输出字典序最小的一个。
Sample Input
2
3
Computer 3 3
English 20 1
Math 3 2
3
Computer 3 3
English 6 3
Math 6 3
Sample Output
2
Computer
Math
English
3
Computer
English
Math
Hint
在第二个样例中,按照 Computer->English->Math 和 Computer->Math->English 的顺序完成作业,所扣的分数都是 3,由于 English 的字典序比 Math 小,故输出前一种方案。
二、思路概述
- 用f[S]表示写了S作业,要扣的最少分,用sum[S]表示写了S作业,要花的时间,pre[S]来表示到了S状态,最后更新的作业(用于回溯)。
- 转移方程
sum = S 作业集合对应的总时间
f[S|(1<<x)] = f[S] + tmp(作业 x 被扣的分数)
c[x] = 作业 x 完成所需时间
d[x] = 作业 x 的 DDL
tmp = max ( sum + c[x] – d[x], 0 ) - 枚举每一个作业总和S,对每一个S,枚举它里面的每一项作业,看是否能进行转移。
三、细节
- 用二进制编码来表示状态的相关操作:
2.对每一个编号为i的点,应该是1<<i来表示这个状态,而不是>>。
四、完整代码
/*先枚举只写两份作业的最短时间,
再枚举只写三份作业的时间....
最后得到的结果就是写所有作业的时间*/
#include<iostream>
#include<algorithm> //fill函数,sort函数,max函数
#include<set>//set函数,(去除重复)
#include<map>//不同类型转换
#include<cstring>//memset函数
using namespace std;
const int inf=1e9;
int f[50010],sum[50010],pre[50010];//f[S]表示在S这一状态下扣的最少分 t[S]表示花的时间
struct hwork{
string name;
int time;
int ddl;
}hw[20];
void output(int s){
if(s==0)return;
//cout<<"yes"<<endl;
output(s-(1<<pre[s]));
cout<<hw[pre[s]].name <<endl;
}
int main(){
int n;cin>>n;
while(n--){
int m;cin>>m;
memset(f,0,sizeof(f));
memset(sum,0,sizeof(sum));
//memset(sum,0,sizeof(sum));
for(int i=0;i<m;i++){
string nm;cin>>nm;hw[i].name=nm;
int dl;cin>>dl;hw[i].ddl=dl;
int ti;cin>>ti;hw[i].time=ti;
//int mm=1<<(i);sum[mm]=ti;
}
int num=(1<<m)-1;
//cout<<num<<endl;
for(int s=1;s<=num;s++){//枚举事件的总和S
f[s]=inf;//f[s]的值设的很大,便于初始更新
//cout<<s<<"s"<<endl;
for(int j=m-1;j>=0;j--){//枚举每一件事
int x=1<<j;
if(!(s&x))continue;//s事件和里面不包括事件j
int temp=max(sum[s-x]+hw[j].time-hw[j].ddl,0);
//cout<<sum[s]+hw[j].time-hw[j].ddl<<"temp另外"<<endl;
//cout<<j<<"j"<<endl;cout<<sum[s]<<"sum"<<endl;
//cout<<temp<<"temp"<<endl;
if(f[s]>f[s-x]+temp){
f[s]=f[s-x]+temp;//事件s最少超时
//cout<<f[s]<<endl;
sum[s]=sum[s-x]+hw[j].time ;//sum里面存的是事件s所用的时间总和
//cout<<sum[s]<<"sum222222222"<<endl;
pre[s]=j;//事件的最晚事件是j
}
}
}
cout<<f[num]<<endl;
//cout<<"hellp"<<endl;
output(num);
}
}