HDU 1074(状态压缩dp)

//起初想到贪心没思路 看了题解 状态压缩DP 最多15个状态
// [1, 1<<n) 枚举各个状态推出的每种可能即可
//注意题意说如果答案相同输出字典序最优的答案
//只需要在枚举的时候处理下就好 这里理解了好一会儿 
#include <iostream>
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "algorithm"
#include <queue>
#include <stack>
#define N (1 << 15) + 5
#define INF 1<<30
using namespace std;
int dp[N], Time[N], pre[N], n, ans; //dp[N]存放到达各个状态最少的扣分数,Time[N]存放到达各个状态扣分最少所需的时间,pre[N]各个状态的前驱
stack<int> sta;

struct node{
    int deadline, cost;
    char name[105];
} subjuct[20]; //各个科目的作业

int main()
{
    int t;
    //freopen("t",  "r", stdin);
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
            scanf("%s%d%d", subjuct[i].name, &subjuct[i].deadline, &subjuct[i].cost);
        ans = 1 << n;
        for(int i = 1; i < ans; i++){
            dp[i] = INF;
            for(int j = n; j >= 1; j--){ //这里是重点 为了保证输出的答案满足字典序最优 这里是j从最后一个作业往前枚举
                int tmp = 1 << (j-1);   //因为一旦j枚举成功 就相当于作业j放在最后完成 
                if(tmp & i)
                {
                    int last = i - tmp; //第j个作业没做的之前的状态
                    int cost = max(Time[last] + subjuct[j].cost - subjuct[j].deadline, 0); //计算做了第J个作业增加的扣分数
                    if(dp[i] > dp[last] + cost) //如果扣分数小于之前的方案就更新
                    {
                        dp[i] = dp[last] + cost;
                        pre[i] = j; //更新前驱
                        Time[i] = Time[last] + subjuct[j].cost;  //更新到达状态i的时间值
                    }
                }
            }
        }
        printf("%d\n", dp[ans - 1]);
        int tmp = ans - 1;
        while(tmp) //找前驱并入栈
        {
            sta.push( pre[tmp] );
            tmp -= 1 << (pre[tmp] - 1);
        }
        while(!sta.empty()) //输出结果
        {
            printf("%s\n", subjuct[sta.top()].name);
            sta.pop();
        }
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值