HDU 1074 Doing Homework 状态压缩dp

http://acm.hdu.edu.cn/showproblem.php?pid=1074

题目是给定n个科目(n <= 15)然后每个科目有最迟完成时间和需要多少天去完成。

现在要你安排一个顺序去做这些科目,使得扣分最小,要求输出字典序最小的解。

 

考虑用dp[i]表示完成了i的二进制那些科目时,所需的最小扣分。比如i(1001)就是完成了第1科和第四科。

那么枚举这样的每一个状态,再枚举每一个科目,如果当前这个状态还没有完成这个科目的话,就是(i & (1 << j)) == 0的话。

就可以进行转移了。可以用dp记录很多信息,包括当前状态的前一个状态(用于记录路径)和当前状态是选了哪一个科目(每一个新状态完成一个科目)和完成了当前状态的那些科目后,所需的最小扣分。和完成了当前状态的那些科目后,用了多少时间(这个用来记录后面枚举新科目的时候,是否会扣分)

其实你想维护什么,dp那里都可以记录。关键是转移的时候要按照扣分的多少来转移就是了。

如果从10001枚举到10011所需的扣分比从10010枚举到10011所需的最小扣分要小,


那么就要更新了,但是反过来,如果是相等呢?

相等的话,是不用变的,

因为答案需要的是最小字典序解

 

从10001枚举到10011

就是先做1和5,再做4

从10010枚举到10011

就是先做1和4,再做5

 

他们的cost相同,但是明显下面的字典序比较小(因为题目已经排好序)

所以,是不用变了

最后,dp数组要开到 1 << 15

一开始开了15,一直TLE

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
struct nodeInformation {
    char name[100 + 20];
    int dead;
    int need;
}a[15 + 20];
struct nodeDp {
    int pre;
    int cur; //当前这个是什么

    int cost; //记录扣多少分
    int tim; //什么时候完成了这个状态
}dp[1 << 16];
void show(int val) {
    if (val == 0) return;
    show(dp[val].pre);
    printf("%s\n", a[dp[val].cur].name);
}
void work() {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) {
        scanf("%s%d%d", a[i].name, &a[i].dead, &a[i].need);
    }
    int end = 1 << n;
    memset(dp, 0x3f, sizeof dp);
    dp[0].cost = 0;
    dp[0].pre = -inf;
    dp[0].tim = 0; //一科都没完成的状态
    dp[0].cur = -inf;
    for (int i = 0; i < end; ++i) { //枚举每一个状态
        for (int j = 0; j < n; ++j) {
            if ((i & (1 << j)) == 0) { //如果没完成到这个科目,才能转移
                int newState = i | (1 << j);
                int dayTofinish = dp[i].tim + a[j].need;
                int cost = dayTofinish - a[j].dead;
                if (cost < 0) cost = 0; //就是提前完成了,不用扣费
                cost += dp[i].cost;

                if (cost < dp[newState].cost) { //现在这样花费比以前的小
                    dp[newState].cost = cost;
                    dp[newState].cur = j;
                    dp[newState].pre = i; //i这个状态
                    dp[newState].tim = dayTofinish;
                }
            }
        }
    }
    printf("%d\n", dp[end - 1].cost);
    show(end - 1);
}

int main() {
#ifdef local
    freopen("data.txt","r",stdin);
#endif
    int t;
    scanf("%d", &t);
    while (t--) work();
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/liuweimingcprogram/p/5999414.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值