用状态压缩DP来做
首先最多有15个作业,每个作业有做或者没做两个状态,所以可以用二进制15位整数来表示当前状态
dp的时候就计算要达到当前状态最少扣的分,d[u]=d[去掉第i个作业的状态]+(第i个作业会扣的分)
dp的时候要保存父节点和作业,用来输出
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stack>
using namespace std;
const int N=(1<<15)+10;
const int INF=0x7f7f7f7f;
int n;
string name[16];
int dl[16],cost[16];
int d[N],f[N],op[N];
int dp(int u)
{
if(d[u]!=-1) return d[u];
d[u]=INF;
int vis[16],t=0,temp=u;
for(int i=n-1;i>=0;i--) //列出哪些作业做了,还有总花费时间
{
vis[i]=temp%2;
temp/=2;
if(vis[i])
t+=cost[i];
}
for(int i=n-1;i>=0;i--) //从后往前,所以反字典序
if(vis[i]) //选择做第i个作业
{
int v=dp(u-(1<<(n-i-1)));
if(t>dl[i]) v+=t-dl[i]; //加上当前状态做完第i个作业会扣多少分
if(v<d[u])
{
d[u]=v;
f[u]=u-(1<<(n-i-1)); //记录父节点
op[u]=i; //记录作业
}
}
return d[u];
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
cin>>name[i];
scanf("%d%d",&dl[i],&cost[i]);
}
memset(d,-1,sizeof(d));
d[0]=0;
printf("%d\n",dp((1<<n)-1));
stack<int> s;
int u=(1<<n)-1;
while(u)
{
s.push(op[u]);
u=f[u];
}
while(!s.empty())
{
cout<<name[s.top()]<<endl;
s.pop();
}
}
return 0;
}