HDU 1704 二进制的应用于状压DP

Ignatius has just come back school from the 30th ACM/ICPC. Now he has a lot of homework to do. Every teacher gives him a deadline of handing in the homework. If Ignatius hands in the homework after the deadline, the teacher will reduce his score of the final test, 1 day for 1 point. And as you know, doing homework always takes a long time. So Ignatius wants you to help him to arrange the order of doing homework to minimize the reduced score.

Input

The input contains several test cases. The first line of the input is a single integer T which is the number of test cases. T test cases follow. 
Each test case start with a positive integer N(1<=N<=15) which indicate the number of homework. Then N lines follow. Each line contains a string S(the subject's name, each string will at most has 100 characters) and two integers D(the deadline of the subject), C(how many days will it take Ignatius to finish this subject's homework). 

Note: All the subject names are given in the alphabet increasing order. So you may process the problem much easier. 

Output

For each test case, you should output the smallest total reduced score, then give out the order of the subjects, one subject in a line. If there are more than one orders, you should output the alphabet smallest one. 

Sample Input

2
3
Computer 3 3
English 20 1
Math 3 2
3
Computer 3 3
English 6 3
Math 6 3

Sample Output

2
Computer
Math
English
3
Computer
English
Math

大意:
有n门课程作业,每门作业的截止时间为D,需要花费的时间为C,若作业不能按时完成,每超期1天扣1分。
这n门作业按课程的字典序先后输入 ,问完成这n门作业至少要扣多少分,并按字典序从小到大输出扣分最少的做作业顺序。

 

如何实现DP,观察数量级n<=15,是不是可以枚举所有的情况来判段哪些作业的先后顺序,1<<15,完全可以开一个数组存放

那这样说的话就可以枚举00…… 1到1111…… 1这些作业的情况,当然所有的作业是都需要做完的,用1表示做了的作业,那么1左移 的每一位都代表这每种作业的做与不做的情况。

dp[i]表示状态为i(二进制的情况)最小扣分情况

#include<bits/stdc++.h>
#include<string>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define ms(a,b) memset(a,b,sizeof a)
const int INF=0x3f3f3f3f;
struct node
{
    //char project[200];
    string project;//课程
    int dead;//最后的期限
    int need;//需要做完的时间
}num[20];
struct node1
{
    int pre;//先前转态,就是从哪一个状态推出当前状态的
    int cost;//做完当前状态的最小扣分
    int time;//所需要的总时间
    int now;//当前所选取的作业
}dp[1<<15];
int n;
int main() {
    int t;
    ios::sync_with_stdio(0);cin.tie(0);
    cin>>t;
    while(t--)
    {
        cin>>n;
        //ms(num,0);
        ms(dp,0);
        rep(i,0,n-1){
        cin>>num[i].project;
        cin>>num[i].dead;
        cin>>num[i].need;
        }
        int m=1<<n;
        for(int i=1;i<m;i++)
        {
            dp[i].cost=INF;
            for(int j=n-1;j>=0;j-)//可以从最后一个状态往前推哪一个作业,比如:i=101,temp=100
            {//那么当前i这种作业情况是temp推出来的,也就是做完1和3个作业是在做完3号作业的
                int temp=1<<j;//的前提下才有的
                if(temp&i)
                {
                    int p=i-temp;//那就看1号作业的情况
                    int tt=dp[p].time+num[j].need-num[j].dead;//扣分=写完前一种作业的
//时间+写完当前作业需要的时间-要交当前作业的时间
                    if(tt<=0) tt=0;//如果是负数,就是说能够在交作业之前写完,那样置零,不会扣分
                    if(tt+dp[p].cost<dp[i].cost)
                    {
                        dp[i].cost=tt+dp[p].cost;//最小化扣分
                        dp[i].pre=p;//记录父节点
                        dp[i].now=j;//记录是选了哪一门
                        dp[i].time=dp[p].time+num[j].need;//需要的时间
                    }
                }
            }
        }
//下面需要自行体会这个按着字典序从小到大输出的方法,你可以这样理解,给出的样例是已经排好的字典序
//我们是从后往前推的dp,因此这样输出的顺序正好是从大到小的顺序,那就需要翻转一下。
        cout<<dp[m-1].cost<<endl;
        stack<int>ss;
        int k=m-1;
        while(dp[k].time)
        {
            //cout<<num[dp[k].now].project<<endl;
          ss.push(dp[k].now);
            k=dp[k].pre;

        }
        while(ss.size())
        {
            int k=ss.top();
            cout<<num[k].project<<endl;
            ss.pop();
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值