题目链接:https://vjudge.net/problem/HDU-1074
转自:https://blog.csdn.net/qq_41925919/article/details/89372016
题意:有一些作业的截止时间和完成作业需要的时间,一门作业每超过截止时间一天就扣一分。求扣分最少且字典序最小的输出序列。输入按照字典序输入。
思路:要么贪心要么dp。 这道题是dp。因为n<15,所以可以用一个二进制数字进行状压。输入是字典序,要求字典序最小,就必须倒着枚举。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
const int inf=0x3f3f3f3f;
struct NODE
{
char s[105];
int pre,time,score,now,dead,cost;
} e[1<<16];
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",e[i].s,&e[i].dead,&e[i].cost);
}
int alls=1<<n;
for(int i=1;i<alls;i++)//枚举状态
{
e[i].score=inf;
for(int j=n-1;j>=0;j--)//因为要求字典序最小,所以要从字典序最大的开始枚举
{
if((1<<j)&i)//已经做了这门课
{
int past=i-(1<<j);//上一个状态 即没有做这门课的时候
int alltime=e[past].time+e[j].cost-e[j].dead;
alltime=max(0,alltime);
if(e[past].score+alltime<e[i].score)//可以更新答案
{
e[i].score=e[past].score+alltime;
e[i].now=j;//记录当前状态应该做哪一门课
e[i].pre=past;//记录当前状态的前驱
e[i].time=e[past].time+e[j].cost;
}
}
}
}
printf("%d\n",e[alls-1].score);
int k=alls-1;
stack<int> st;
while(k)
{
st.push(e[k].now);
k=e[k].pre;
}
while(!st.empty())
{
printf("%s\n",e[st.top()].s);
st.pop();
}
}
return 0;
}