HDU 1074 (状态压缩dp)详解

题目意思:Ignatius同学,快到期末了结果还有好多课程没有完成,每门课作业都有对应的截至日期,和完成它所需的时间,超过截至日期后,没多一天扣1分,现在文他该如何安排写作业的顺序使得被扣的分最少?

输入要求:T组数据,N门课,每门课的截至日期和完成所需的时间

输出要求:被扣的分,和写作业的顺序。(如果有不同顺序扣分相同的出现,输出字典序)

题目提示:1.因为最多只有15门课,所以1<<15在一个可以接受的范围内,但是15!太大,用枚举必定超时
2.输入时是按照字典序输入的

#include <iostream>
#include <cstring>
#include <stack>
#include <algorithm>
using namespace std;

const int INF=0x3f3f3f3f;//四个3f

struct node
{
    string name;
    int deadline;
    int cost;
} homework[20];

struct kode
{
    int time;//记录时间
    int score;//此时被扣的分
    int pre;//它上面的一门课的编号
    int now;//现在这门课的编号
} dp[1<<15];

int main()
{
    int T;
    int i,j;
    int s,n,end;
    scanf("%d",&T);
    while(T--)
    {
        memset(dp,0,sizeof(dp));
        scanf("%d",&n);
        for(i=0; i<n; i++)//建议从0开始,因为状态dp 1.....1最后一个1为2的0次方
        {
            cin>>homework[i].name>>homework[i].deadline>>homework[i].cost;
        }
        end=1<<n;
        for(s=0;s<=end-1;s++)//end-1=1....1 n个1  代表所有作业已经完成了的状态 0代表没有作业完成 必须从小到大,
        //后面需要利用前面求出来的temp1+dp[past].score<dp[s].score
        {
            int temp;//用来记录现在是选择第i门课时的状态
            dp[s].score=INF;//因为要求最小值,所以刚开始把他们初始化为无限大
            dp[0].score=0;//初始化,做第一门课之前时间为0;
            for(i=n-1;i>=0;i--)//因为要求字典序输出,所以必须从大到小
            //假设有两门课,完全相同,因为输入时是按照字典序输入的,所以开头字母大的必定序号大
            //然后现进行判断,因为状态是从小往大递增的,刚开时小的已经进入,当状态大到两者都包括时,序号大的课,先进入该状态
            //然后再到序号小的,而到序号小的时候因为他们截至日期和耗费时间完全一样下面的
            //temp1+dp[past].score==dp[s].score因此的序号大的位置不改变,但输出时是按照状态从小到大输的,所有序号小的仍然在前面 
            {
                temp=1<<i;//0...100...000,1距离最右边有i个数位
                if(s&temp)//如果s的第i位也为0,也就是该状态下,包含第i个门课,就进入if
                {
                    int past=s-temp;//s-i
                    //例如000011111
                    //   000010000
                    //  =000001111刚好就是不学第i门课时的状态
                    int temp1=dp[past].time+homework[i].cost-homework[i].deadline;
                    //之前的时间加上这门课需要耗费的时间
                    if(temp1<0)//因为有的课会提前完成,提前完成意味着不扣分,所以为0
                    {
                        temp1=0;
                    }
                    if(temp1+dp[past].score<dp[s].score)//如果先做这门课,比之前的情况更少扣分,那么记录下来
                    {
                        dp[s].now=i;
                        dp[s].pre=past;
                        dp[s].score=dp[past].score+temp1;
                        dp[s].time=dp[past].time+homework[i].cost;//记录时间
                    }
                }
            }
        }
        stack<int> S;//构造一个栈
        int temp2=end-1;
        cout << dp[temp2].score << endl;
        while(temp2)
        {
            S.push(dp[temp2].now);//将i门课的学习顺序以此存入栈中
            temp2=dp[temp2].pre;
        }
        while(!S.empty())
        {
            cout << homework[S.top()].name << endl;//因为刚才放的时候,全部学完时的状态在最下面,最上面是s=1时,那门课的序列
            S.pop();
        }
    }
    return 0;
}     


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值