HDU - 1074 Doing Homework(状压DP)

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

题目大意: 一共有N种作业要去作, 每个作业都有一个最后期限,和要完成需要花费的时间。每项作业晚交一天,就会有一天的罚时, 问你怎么安排作业才能让罚时最少, 并输出完成作业是先后顺序。
大体思路: 因为作业数量不多, 用int的15位表示表示一项作业是否完成(1代表完成,0代表没完成, 可能和一些人表示法不一样), 枚举出所有方案并动态规划。

#include <bits/stdc++.h>

using namespace std;
struct info//用来记录输入的结构体
{
    char name[150];//作业的名字
    int dea;//最后期限
    int tim;//完成所需的时间
}a[20];
struct node//用于dp的结构体
{
    int penalty;//记载罚时
    int time;//记载此状态下当前时间
    int pre;//上一项完成的作业,指向a的下标
    int now;//记载当前完成的作业
}dp[(1 << 15) + 1];
void _back(int n)//用于回溯输出完成方案
{
    if(dp[n].pre == -1)
    {
        return;
    }
    _back(dp[n].pre);
    printf("%s\n", a[dp[n].now].name);
}
int main()
{
    int t, n;
    while(scanf("%d",&t)!= EOF)
    {
        while(t-- && scanf("%d", &n))
        {
            for(int i = 0; i < n; i++)
            {
                scanf("%s %d %d", a[i].name, &a[i].dea, &a[i].tim);
                getchar();
            }
            int enu = (1 << n);//所有可行的enu - 1种方案
            for(int i = 0; i <= enu; i++)//结构体初始化, 罚时设为无穷
            {
                dp[i].penalty = 0x3f3f3f3f;
                dp[i].time = 0;
            }
            dp[0].penalty = 0;
            dp[0].pre = -1;//标记最开始的状态
            for(int i = 1; i < enu; i++)//枚举各种方案
            {
                for(int j = n - 1; j >= 0; j--)//把int 15位跑一遍
                {
                    int tmp = (1 << j);//将1送到j位
                    if(tmp & i)//判断这位是否为1
                    {
                        int past = i - tmp;//上一个此作业未完成的状态
                        int x = dp[past].time + a[j].tim - a[j].dea;//从上一状态到这一状态的罚时
                        x = max(0, x);
                        if(x + dp[past].penalty < dp[i].penalty)//从上一状态到这一状态罚时小于当前已有的罚时时更新
                        {
                            dp[i].penalty = x + dp[past].penalty;
                            dp[i].time = dp[past].time + a[j].tim;
                            dp[i].pre = past;//记载路径,方便back函数回溯
                            dp[i].now = j;
                        }
                    }
                }
            }
            printf("%d\n", dp[enu - 1].penalty);
            _back(enu - 1);//回溯输出路径
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值