Loading

HDU 1074 Doing Homework (状压DP)

Doing Homework

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 14600    Accepted Submission(s): 7070

 

Problem Description

 

Ignatius has just come back school from the 30th ACM/ICPC. Now he has a lot of homework to do. Every teacher gives him a deadline of handing in the homework. If Ignatius hands in the homework after the deadline, the teacher will reduce his score of the final test, 1 day for 1 point. And as you know, doing homework always takes a long time. So Ignatius wants you to help him to arrange the order of doing homework to minimize the reduced score.

 

 

Input

 

The input contains several test cases. The first line of the input is a single integer T which is the number of test cases. T test cases follow.
Each test case start with a positive integer N(1<=N<=15) which indicate the number of homework. Then N lines follow. Each line contains a string S(the subject's name, each string will at most has 100 characters) and two integers D(the deadline of the subject), C(how many days will it take Ignatius to finish this subject's homework).

Note: All the subject names are given in the alphabet increasing order. So you may process the problem much easier.

 


Output

 

For each test case, you should output the smallest total reduced score, then give out the order of the subjects, one subject in a line. If there are more than one orders, you should output the alphabet smallest one.

 


Sample Input

2
3
Computer 3 3
English 20 1
Math 3 2
3
Computer 3 3
English 6 3
Math 6 3

Sample Output

2
Computer
Math
English
3
Computer
English
Math

题目大意

作业就要做不完了,每项作业(已经按照字典序排好)都有一个花费时间和限定时间,在限定时间之后在交的作业,每天扣一分,现在扣分最小值和相应的作业顺序(相同最小值时,以字典序最小的情况输出)

题目分析

注意到最多只有15个作业,所以可以利用一个二进制数来表示当前的完成情况:比如111是全完成,101是第二个没完成

假如有7个作业,那么二进制就是 1111111 也就是 2^7-1 即127 我们枚举从1到127 用dp[i]来表示达到当前状态所扣分数的最小值

然后j从后向前枚举单个作业(至于为什么从后向前之后再说),判断这个作业是不是在当前状态已经完成了的,比如101,就是第一个作业和第三个作业是已经完成了的,那么当前的状态就可以从前一个状态转化过来,比如101 就可以从100或者001转化过来 这样就可以进行更新了,显然的 dp[i]=max(dp[i],dp[前一个状态]+a[j])

现在可以解释为什么从后向前遍历单个作业了,因为根据上面的转移方程,我们可以看出来当前状态是在完成这个作业之后达到的,也就是说,这个作业是最后完成的,而题目中要求,如果遇到最小值相同的时候,要以字典序最小的方案输出,所以必须要从后往前遍历

当然,要输出做题顺序,就要每次都记录当前状态的上一个状态是什么,以及当前完成了什么作业。

代码:

#include <bits/stdc++.h>  

using namespace std; 

typedef struct 
{
    string name;
    int dday;
    int cost;
}homework;

struct 
{
    int pre;
    int name;
    int vul;
    int time;
}dp[1<<15];

homework a[105];
int tot,t,n,i,j;
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n;
        memset(dp,0,sizeof(dp));
        for(i=0;i<n;i++)
        {
            cin>>a[i].name>>a[i].dday>>a[i].cost;
        }
        tot=1<<n;
        for(i=1;i<tot;i++)
        {
            dp[i].vul=0x7fffff;
            for(j=n-1;j>=0;j--)
            {
                int temp=1<<j;
                if(temp&i)
                {
                    int s=dp[i-temp].time+a[j].cost-a[j].dday;
                    if(s<0)
                    s=0;
                    if(dp[i].vul>dp[i-temp].vul+s)
                    {
                        dp[i].pre=i-temp;
                        dp[i].name=j;
                        dp[i].vul=dp[i-temp].vul+s;
                        dp[i].time=dp[i-temp].time+a[j].cost;
                    }
                }
            }
        }
        cout<<dp[tot-1].vul<<endl;
        i=tot-1;
        stack<int>q;
        while(dp[i].time)
        {
            q.push(dp[i].name);
            i=dp[i].pre;
        }
        while(!q.empty())
        {
            cout<<a[q.top()].name<<endl;
            q.pop();
        }
    }
}

 

 

posted @ 2019-08-15 01:28  WinterFa1L  阅读(262)  评论(0编辑  收藏  举报