hdu1074

[kuangbin带你飞]专题十二 基础DP1
D题
题意: 有n项作业,每项作业需要时间c去完成,截至日期是在d,每一项作业每超过截至时间就会扣1分,现在问如何安排作业能使得扣的分最少,并且当扣分相同时,要选择字母序最小的那个作业安排。

思路:(用搜索思维去推dp)
如果有暴力搜索的想法去,那就是把1>>n种安排都暴力搜一遍,第一种就是直接按顺序的安排过去(题目说Note: All the subject names are given in the alphabet increasing order. So you may process the problem much easier.)而减枝就是把当前扣的分已经大于目前得到的最优答案时,就停止搜索,这个从总体看过去,就是把有一脉有相同特征的直接剪掉。

而用搜索会tle,那能否转化为dp,毕竟有个说法递归都可以转化为循环。

枚举1>>n种安排,每种安排创造了特征形态,其他安排都是在一种安排上的衍生。假设一种安排已经完成了m项任务,那么它有m种衍生源可能,就是它的上一个完成的任务有m种可能。
用二进制数A表示安排,如 A=1110001表示完成了第1,2,3,7项任务,它的衍生源可能是110001,1010001,1100001,1110000,即可假设上一个完成的任务是b,那么它的衍生源表示为 A- 1<<(n-b),如1110001- 1<<(7-2)=1010001。
每种安排继承最优衍生源,同时记录每次继承的衔接任务是哪一项,如此在最终得到完成所有任务的安排时,可以根据衔接任务去串联起时间顺序。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
struct Item
{
    string name;
    int c,d;
};
struct Arrange
{
    int task;//衔接任务
    int fa; //衍生源
    int time;//所有已完成安排的时间
    int score;//被扣的分数
};
const int maxn=1<<15+10;
Item task[20];
Arrange arrange[maxn];
int main(void)
{
    int cas,n;
    cin>>cas;
    while(cas--)
    {
        cin>>n;
        for(int i=1;i<=n;++i)cin>>task[i].name>>task[i].d>>task[i].c;

        int num=1<<n; //安排的方案数
        for(int i=1;i<num;++i)
        {
            arrange[i].score=inf;   //对任务初始化,每个任务的扣分数在开始时为无穷大
            arrange[i].time=0;      //              每个任务的时间是0.
            for(int j=n;j>=1;--j)   //j表示衍生源里没有的那个任务,由于要求最小排列顺序,因此要倒着来。
            {
                int tem=1<<(n-j);
                if(tem&i)           //如果当前安排包括这个任务,才能去继续寻找它的衍生源。
                {
                    int temp=i-tem;  //衍生源
                    int add=arrange[temp].time+task[j].c-task[j].d;
                    if(add<0)add=0;
                    if(arrange[temp].score+add<arrange[i].score)   //寻找最优衍生源
                    {
                        arrange[i].score=arrange[temp].score+add;
                        arrange[i].time=arrange[temp].time+task[j].c;
                        arrange[i].task=j;
                        arrange[i].fa=temp;
                    }

                }

            }

        }
        cout<<arrange[num-1].score<<endl;
        int q[20];
        int tt=num-1;
        int th=n+1;
        while(arrange[tt].time)
        {
            q[--th]=arrange[tt].task;
            tt=arrange[tt].fa;

        }
        for(int i=1;i<=n;++i)cout<<task[q[i]].name<<endl;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

是Mally呀!

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

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

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

打赏作者

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

抵扣说明:

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

余额充值