1. 程式人生 > >HDU 1074 Doing Homework(狀壓DP+記錄路徑)

HDU 1074 Doing Homework(狀壓DP+記錄路徑)

原題地址

Doing Homework

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

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 Hint
In the second test case, both Computer->English->Math and Computer->Math->English leads to reduce 3 points, but the word "English" appears earlier than the word "Math", so we choose the first order. That is so-called alphabet order.
題意是說,一共給你按名字的字典序排列的n門功課。每一門功課有名字,最晚完成時間,完成所需要的時間。 一門功課的完成時間每超過最晚完成時間1天扣一分,請問把所有功課完成的最少扣分數的多少,並且輸出這種情況下的做功課順序。 如果多種順序的最少扣分數都是最少的,那麼輸出字典序最小的那個。 最多隻有15門功課,可以用狀壓DP。1表示完成,0表示未完成。 列舉所有情況 i ,如果一門功課 j 在 i 狀態下已經完成了,那麼我們比較狀態 i - j 的扣分數和 i 的扣分數。 狀態轉移方程: pass=dp[last].time+hw[j].cost-hw[j].limit;
dp[i].score=min( pass+dp[last].score , dp[i].score );

其中:

pass是在 i - j 狀態下完成 j 功課的扣分數。(只是 j 功課的扣分數)

dp[ i ].score是 i 狀態下的總最小扣分數。

dp[ i ].time是 i 狀態下所用的總時間。

hw[ j ].cost是完成功課 j 所需的時間。

hw[ j ].limit是完成功課 j 的最晚時間。

下面貼程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>

using namespace std;

#define INF 0x3f3f3f3f
#define MAX 1<<15
struct DP
{
    int pre,score,time,now;
} dp[MAX];

struct HW
{
    int cost,limit;
    char name[111];
}hw[33];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        memset(dp,0,sizeof(dp));
        for(int i=0; i<n; i++)
        {
            scanf("%s%d%d",hw[i].name,&hw[i].limit,&hw[i].cost);
        }
        int End=1<<n;
        for(int i=1;i<End;i++)
        {
            dp[i].score=INF;
            for(int j=n-1;j>=0;j--)
            {
                //printf("ok\n");
                int k=1<<j;
                if(k&i)
                {
                    int last=i-k;
                    int pass=dp[last].time+hw[j].cost-hw[j].limit;
                    if(pass<0)
                    {
                        pass=0;
                    }
                    if(pass+dp[last].score<dp[i].score)
                    {
                        dp[i].score=pass+dp[last].score;
                        dp[i].now=j;
                        dp[i].pre=last;
                        dp[i].time=dp[last].time+hw[j].cost;
                    }
                }
            }
        }
        stack <int> s;
        int tmp=End-1;
        printf("%d\n",dp[tmp].score);
        while(tmp)
        {
            s.push(dp[tmp].now);
            tmp=dp[tmp].pre;
        }
        while(!s.empty())
        {
            printf("%s\n",hw[s.top()].name);
            s.pop();
        }
    }
    return 0;
}