有n个任务,每个任务有一个截止时间,超过截止时间一天,要扣一个分。
求如何安排任务,使得扣的分数最少。
Input
有多组测试数据。第一行一个整数表示测试数据的组数
第一行一个整数n(1<=n<=15)
接下来n行,每行一个字符串(长度不超过100)表示任务的名称和两个整数,分别表示任务的截止时间和完成任务需要的天数。 这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
思路:
1. dp[i]表示状态为i所扣的分,time[i]表示状态为i时最短用多长时间,last[i]记录状态i的最后一个任务编号,可以打印路径。
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int del[20],cost[20],time[1<<16],dp[1<<16],n,last[1<<16];
string task[20];
void print(int mask){
if(!mask) return;//mask=0全部任务输出完毕
print(mask-(1<<last[mask])); //输出其前面的状态
cout<<task[last[mask]]<<endl;//输出mask的最后一个任务
}
int main()
{
int t;
cin>>t;
while(t--){
cin>>n;
for(int i=0; i<n; i++){
cin>>task[i]>>del[i]>>cost[i];
}
memset(time,0,sizeof(time));
for(int mask=1; mask<(1<<n); mask++){
dp[mask]=0x7fffffff;
for(int end=0; end<n; end++)//注意字典序小的优先输出,下面更新是有等于符号的。若是逆序,就少个等于号就行了
{
int k=1<<end;
if(!(mask&k))continue;//这里假如写成if((mask&k)==0)mask&k一定要加括号
int sc=time[mask-k]+cost[end]-del[end];
sc=max(0,sc);
if(dp[mask]>=dp[mask-k]+sc){//有等于
dp[mask]=dp[mask-k]+sc;
time[mask]=time[mask-k]+cost[end];
last[mask]=end;
}
}
}
cout<<dp[(1<<n)-1]<<endl;
print((1<<n)-1);
}
return 0;
}