HDU1074 Doing Homework (状压dp,从n!种排列中选一种排列)

题意

有n个任务,每个任务有一个截止时间,超过截止时间一天,要扣一个分。
求如何安排任务,使得扣的分数最少。

解题

n个任务,共有n!种执行方式。因为n不超过15,所以用状态压缩的方式来表示状态(即二进制)。
设dp[s]表示以状态s作为终止状态的安排方式的所扣最少分数。
状态转移方程:dp[s]=min{dp[s],dp[last]+执行x任务所需时间},状态last与s差了一个任务x。
因为要求在所扣分数相同的情况下,输出字典序最小的方案。所以,枚举状态last(通过枚举任务x来实现)需要排序枚举x任务。

AC代码

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <string>
#include <cstring>
#include <stack>
#define INF 0x3f3f3f3f
using namespace std;

const int maxn=20;
struct node
{
    string str;
    int ed,t;
    node(string str,int ed,int t):str(str),ed(ed),t(t){}
    node(){}
}a[maxn];
struct state
{
    int sum;//已扣分数
    int time;//当前时间
    int pre;//上一个状态
    int key;
}b[1<<maxn];

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        int n,cnt=0;
        cin>>n;
        for(int i=0;i<n;i++)
        {
            string str;
            int ed,t;
            cin>>str>>ed>>t;
            a[cnt++]=node(str,ed,t);
        }
        int tar=(1<<n)-1;
        for(int s=1;s<=tar;s++)
        {
            b[s].sum=INF;
            for(int i=n-1;i>=0;i--)//因为时间相等,按任务名字字典序排序
            {
                int tmp=1<<i;
                b[tmp].pre=0;
                b[tmp].key=i;
                if(s&tmp)
                {
                    int last=s-tmp;
                    int add=b[last].time+a[i].t-a[i].ed;
                    if(add<0) add=0;

                    if(add+b[last].sum<b[s].sum)
                    {
                        b[s].sum=add+b[last].sum;
                        b[s].pre=last;
                        b[s].key=i;
                        b[s].time=b[last].time+a[i].t;
                    }
                }
            }
        }
        int x=tar;
        cout<<b[tar].sum<<endl;
        stack<string> sta;
        while(x)
        {
            int k=b[x].key;
            sta.push(a[k].str);
            x=b[x].pre;
        }
        while(!sta.empty())
        {
            cout<<sta.top()<<endl;
            sta.pop();
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值