Doing Homework 【HDU - 1074】【状压DP+DFS输出路径】

题目链接

快速VJ链接入口


  题意:告诉你有N门课,以及每门课的截止时间和需要的学习时间,问的是,我们最少需要少上几天的课。

  思路:当N只有15的时候,很容易想到的是状态压缩,但是,这个状态又表示的是什么?就是已经选好了做这些课的情况下,我们的需要的丢失的最少成绩,此时用DP表示,实现状态的转移。

  我们去遍历所有的状态,分别是做完分别几门课后的状态压缩,然后,我们在做完的课中选取其中一门课,那么除去这门课,就是前一刻的状态,对于前一刻的状态,有其对应的已经学到的时间线——指的是前面的所有时间表都是排满的,如果现在学这门课比之前的状态下的会减少失分的话,那么,就现在选这门课,然后last(上一个状态也要更新),学这门课的时间需求也可能不一样的,所以时间线也要更新,同时,得分会不一样的,就是这个目的。

  然而,处理的重点确实是个细节,WA了一发之后就发现了关键所在,我们考虑在更新新的课的时候,如果从第0门课向第N-1门课推去的话,其实,就是相当于后面的课已经选好了,我们只需要再增添前面的字典序在前的课就行了!当然是反了的!!!我们得尽可能的先选取上前面的课,然后再补上后面的课。这样就能做到字典序升序了。

  记录前面的状态,以及前一个的值,然后,就是用dfs()跑一遍倒叙输出即可(因为是从满状态往前推去的)。


#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN = 15;
int N, tot; //tot是满状态情况:全都取
struct node
{
    string name;
    int Day_End, needs;
    node(string a="", int b=0, int c=0):name(a), Day_End(b), needs(c) {}
}a[maxN];
struct IN_DP
{
    int score, pre, ti, now_id;
    IN_DP(int a=0, int b=0, int c=0, int d=0):score(a), pre(b), ti(c), now_id(d) {}
}dp[(1<<maxN) + 7];
void dfs(int pos)
{
    if(!pos) return;
    dfs(dp[pos].pre);
    cout<<a[dp[pos].now_id].name<<endl;
}
int main()
{
    int T;  scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &N);
        for(int i=0; i<N; i++)
        {
            cin>>a[i].name;     //输入按照字典序升序,输出也要按照字典序升序输出,我们取的话,得在最优的情况下选取最前面的
            scanf("%d%d", &a[i].Day_End, &a[i].needs);
        }
        tot = (1<<N) - 1;   //全部状态了,都已经取完的情况,满状态
        memset(dp, 0, sizeof(dp));
        for(int s=1; s<=tot; s++)   //所有状态
        {
            dp[s].score = INF;  //初始化扣分的情况
            for(int i=N-1; i>=0; i--)   //我们接下来是选择修学那门课,由于输出按字符串大小输出,故每次完成i的话,就要把i放在最后面
            {
                int tmp = (1<<i);
                if(tmp & s)
                {
                    int las = s - tmp;  //目的就在于先取前面的值,对应i,从大数开始,这就可以保证其s(状态)的补集是从小数开始,而我们的状态从1开始,则即是从最前面的开始选取过来,保证字典序
                    int need = dp[las].ti + a[i].needs - a[i].Day_End;  //所需要的时间:上一个状态的时间线已经到哪了、如果要学这门课的话,是需要额外多少时间(可能是多出来的时间就是指学不全)
                    if(need < 0) need = 0;  //时间不可逆
                    if(dp[s].score > dp[las].score + need)
                    {
                        dp[s] = IN_DP(dp[las].score + need, las, dp[las].ti + a[i].needs, i);
                    }
                }
            }
        }
        printf("%d\n", dp[tot].score);
        dfs(tot);
    }
    return 0;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值