题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1074
思路:暴力。但是N=15,15!的暴力必然超时。问题为求完成N个作业后的最短罚时,考虑如果知道N-1个的最短罚时,能否求出N个作业的最短罚时,发现可行,把N个作业是否完成用二进制表示,则复杂度降为(2^N)。
输出路径,网传的很多代码都是错的,数据不够强。要输出的字典序最小,倒着找必须遍历所有可能性,因为后面贪心找字典序大的可能导致第一个课程的字典序比答案要大,所以要写个DFS遍历一次。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n,ans[16],ct,temp[16],MAX;
struct node
{
int sum;
int res;
}dp[1<<15];
struct node1
{
string s;
int d,c;
}info[16];
bool cmp(node1 a,node1 b)
{
return a.s>b.s;
}
void init()
{
memset(dp,-1,sizeof(dp));
dp[0].sum=dp[0].res=0;
ct=0;
MAX=-1;
}
void read()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>info[i].s>>info[i].d>>info[i].c;
}
sort(info,info+n,cmp);
}
void rundp()
{
for(int i=1;i<(1<<n);i++)
{
for(int j=0;j<n;j++)
{
int j1=1<<j;
if(i&j1)
{
int pre=i-j1;
int cost=dp[pre].sum+info[j].c-info[j].d;
if(cost<0) cost=0;
if(dp[pre].res+cost<dp[i].res||dp[i].res==-1)
{
dp[i].res=dp[pre].res+cost;
dp[i].sum=dp[pre].sum+info[j].c;
}
}
}
}
}
void dfs(int now,int depth)
{
if(now==0)
{
if(MAX<temp[depth-1])
{
MAX=temp[depth-1];
for(int i=0;i<depth;i++) ans[i]=temp[i];
}
return ;
}
for(int i=0;i<n;i++)
{
int j1=1<<i;
if(j1&now)
{
int pre=now-j1;
int cost=dp[pre].sum+info[i].c-info[i].d;
if(cost<0) cost=0;
if(dp[pre].res+cost==dp[now].res)
{
temp[depth]=i;
dfs(now-j1,depth+1);
}
}
}
return ;
}
void print()
{
printf("%d\n",dp[(1<<n)-1].res);
int state=(1<<n)-1;
for(int i=n-1;i>=0;i--)
{
cout<<info[ans[i]].s<<endl;
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
init();
read();
rundp();
dfs((1<<n)-1,0);
print();
}
return 0;
}