第一次做状态压缩dp。
这里注意一个点就是枚举n个作业的顺序不同从而使得更新条件不同。
当正向也就是1-n枚举时候,dp[i]>=dp[i-temp]+score更新。
当逆向也就是n-1枚举的时候,dp[i]>dp[i-temp]+score更新。
题目要求多解下输出最小字典序,而且输入是按照字典序从小到大输入的,当逆序遍历时候,因为输出的是x-(1<<pre[x])这个状态,要想其越小,pre[x]越大,即j越大,所以找到可更新的j就停止,这样保证j是大的
#include <cstring>
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=(1<<15)+5;
struct P
{
char s[105];
int d,t;
}p[20];
int T,n;
int dp[maxn],pre[maxn],time[maxn];
void output(int x)
{
if(!x) return ;
output(x-(1<<pre[x]));
printf("%s\n",p[pre[x]].s);
}
int main()
{
scanf("%d",&T);
while(T--)
{
memset(time,0,sizeof(time));
memset(pre,0,sizeof(pre));
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%s%d%d",&p[i].s,&p[i].d,&p[i].t);
int state=1<<n;
for(int i=1;i<state;i++)//先枚举所有状态
{
dp[i]=INF;//初始化这个状态的值
for(int j=0;j<n;j++)
{
int temp=1<<j;//做这个作业
if(!(i&temp)) continue ;//表示如果不能从i-temp转移到i,也就是做了j也打不到状态i就跳过
int score=time[i-temp]+p[j].t-p[j].d;//i-temp表示从i-temp到i这个状态,也就是做了j这个作业
//score表示如果做了j扣的分,time表示到达这个状态花的时间
if(score<0) score=0;//扣分是负的,表示当前作业都能在规定时间内完成
if(dp[i]>=dp[i-temp]+score)//从i-temp状态到i状态扣的分少就更新
{
dp[i]=dp[i-temp]+score;
time[i]=time[i-temp]+p[j].t;//更新新状态花的时间
pre[i]=j;//记录节点方便输出
}
}
}
printf("%d\n",dp[state-1]);
output(state-1);
}
return 0;
}