【解题报告】 HDU - 1074 Doing Homework(状压dp入门)

本文介绍如何使用状态压缩动态规划(状压DP)解决课程作业安排问题,以求得最小罚时并确保完成顺序的字典序最小。通过枚举所有状态,更新状态以保存最小罚时和完成顺序,最终输出最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题地址

题意

按照名字的字典序给出n门课程作业的截止时间和小明完成需要的时间,如果在超过截止时间之后完成作业则会罚时超出的时间,问怎样安排完成作业的顺序得到最小罚时,并按照字典序输出完成作业的顺序。

思路

这道题目其实一拿到就知道用贪心解决,每次完成截止时间最近的作业就行了,但是贪心可以得到正确的最小罚时,却无法保证完成作业的顺序的字典序最小。而这道题目的n的范围很小,所以想到状压dp,虽然可能有点小题大做的感觉,但是这确实是一道状压dp的入门好题,我们用dp来表示状态,每一次的状态就可以存很多信息,然后枚举从1到(1<<n) - 1,每一个二进制表示一个状态,每一位上1代表该门课程写完,0代表没有完成,则每一个状态可以由该状态减去每一门完成的作业的罚时最小值得到,然后根据上一状态更新该状态,最后dfs输出完成作业的顺序,即得到答案。注意条件判断的时候要加上=,否则deadline相同的课程的输出顺序和答案是相反的。

#include <iostream>
#include <cstdio>
#define inf 0x3f3f3f3f
using namespace std;
const static int maxn = 16;

struct homework
{
    char sub[100];
    int dl;
    int cost;
}hw[maxn];

struct status
{
    int cur;//current time
    int next;//next status
    int pos;//homework num
    int sum;//total decrease score
}dp[1<<maxn];
void put_out(int s)
{
    if(dp[s].pos == -1)
        return;
    put_out(dp[s].next);
    printf("%s\n", hw[dp[s].pos].sub);
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int n;
        scanf("%d", &n);
        for(int i=0; i<n; i++)
        {
            scanf("%s %d %d", hw[i].sub, &hw[i].dl, &hw[i].cost);
        }
        int s = 1<<n;
        dp[0].cur = 0, dp[0].pos = -1, dp[0].sum = 0, dp[0].next = -1;
        for(int i=1; i<s; i++)
        {
            dp[i].sum = inf;
            for(int j=0; j<n; j++)
            {
                if((1<<j)&i)
                {
                    int k = i-(1<<j);
                    int v = max(0, dp[k].cur+hw[j].cost-hw[j].dl);
                    if(dp[i].sum >= dp[k].sum+v)
                    {
                        dp[i].sum = dp[k].sum+v;
                        dp[i].cur = dp[k].cur+hw[j].cost;
                        dp[i].next = k;
                        dp[i].pos = j;
                    }
                }
            }
        }
        printf("%d\n", dp[s-1].sum);
        put_out(s-1);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值