题意
按照名字的字典序给出n门课程作业的截止时间和小明完成需要的时间,如果在超过截止时间之后完成作业则会罚时超出的时间,问怎样安排完成作业的顺序得到最小罚时,并按照字典序输出完成作业的顺序。
思路
这道题目其实一拿到就知道用贪心解决,每次完成截止时间最近的作业就行了,但是贪心可以得到正确的最小罚时,却无法保证完成作业的顺序的字典序最小。而这道题目的n的范围很小,所以想到状压dp,虽然可能有点小题大做的感觉,但是这确实是一道状压dp的入门好题,我们用dp来表示状态,每一次的状态就可以存很多信息,然后枚举从1到(1<<n) - 1,每一个二进制表示一个状态,每一位上1代表该门课程写完,0代表没有完成,则每一个状态可以由该状态减去每一门完成的作业的罚时最小值得到,然后根据上一状态更新该状态,最后dfs输出完成作业的顺序,即得到答案。注意条件判断的时候要加上=,否则deadline相同的课程的输出顺序和答案是相反的。
#include <iostream>
#include <cstdio>
#define inf 0x3f3f3f3f
using namespace std;
const static int maxn = 16;
struct homework
{
char sub[100];
int dl;
int cost;
}hw[maxn];
struct status
{
int cur;//current time
int next;//next status
int pos;//homework num
int sum;//total decrease score
}dp[1<<maxn];
void put_out(int s)
{
if(dp[s].pos == -1)
return;
put_out(dp[s].next);
printf("%s\n", hw[dp[s].pos].sub);
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n;
scanf("%d", &n);
for(int i=0; i<n; i++)
{
scanf("%s %d %d", hw[i].sub, &hw[i].dl, &hw[i].cost);
}
int s = 1<<n;
dp[0].cur = 0, dp[0].pos = -1, dp[0].sum = 0, dp[0].next = -1;
for(int i=1; i<s; i++)
{
dp[i].sum = inf;
for(int j=0; j<n; j++)
{
if((1<<j)&i)
{
int k = i-(1<<j);
int v = max(0, dp[k].cur+hw[j].cost-hw[j].dl);
if(dp[i].sum >= dp[k].sum+v)
{
dp[i].sum = dp[k].sum+v;
dp[i].cur = dp[k].cur+hw[j].cost;
dp[i].next = k;
dp[i].pos = j;
}
}
}
}
printf("%d\n", dp[s-1].sum);
put_out(s-1);
}
return 0;
}