HDU1074 Doing Homework(状压DP~状态的二进制表示)

来源:互联网 发布:域名到期抢注 编辑:程序博客网 时间:2024/05/23 19:14

DoingHomework

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

Input

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

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

Output

For each test case, you should output the smallest totalreduced 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

  

Hint

In the second test case, bothComputer->English->Math and Computer->Math->English leads to reduce3 points, but the

word "English" appears earlier thanthe word "Math", so we choose the first order. That is so-calledalphabet order.


题意:

给你一些作业完成的截止时间和写完每一项作业需要消耗的时间,如果超过截止时间,每超过一天就减一分。问你怎么安排写作业的顺序可以使得扣分最少,并且输出顺序(如果是多个答案,那么输出字典序最小的那个)。


思路:

这里直接利用了二进制表示完成作业的状态,比如011表示第二第三个作业完成,010表示第二件事完成,那么借助这两者可以写出只完成第一件事的状态001,有没有发现就是3-2=1,,只不过转化成二进制了而已。

为什么用二进制?因为n<=15所以每一件事的全排列是15!,这个是非常大,枚举法是没办法的。其实这里面出现了重复,完成1,2,3和3,2,1等等一系列排序其实是一样的天数,只不过是因为排列的先后而导致了最后答案不一样,毕竟最后所有作业都要上交的,所以消耗的天数是一样的。这就间接地引导我们去只用二进制表示这些顺序问题,1代表第i个作业在写,0表示没写。这样的话就是2的15次方,不会超时,我们每一次去比较时就去判定上一次的状态和这一次的关系,记录此时的状态(可以理解为较为优秀的解,因为第二重循环结束找到的是最优解),这是为了输出,具体的细节代码见。


代码:

#include <iostream>#include <string>#include <cstring>#include <stack>#include <algorithm>using namespace std;const int inf = 1 << 30;struct node{string name;int dead, cost;} a[50];struct kode{int time, score, pre, now;} dp[1 << 15];//表示当前状态的一些内容int main(){int t, i, j, s, n, end;cin >> t;while (t--){memset(dp, 0, sizeof(dp));cin >> n;for (i = 0; i<n; i++)cin >> a[i].name >> a[i].dead >> a[i].cost;end = 1 << n;//全排列所有的情况数for (s = 1; s < end; s++)//枚举每一种状态,最后就是所有作业都写完了{dp[s].score = inf;for (i = n - 1; i >= 0; i--){int tem = 1 << i;if (s & tem){    //cout<<s<<"      "<<tem<<endl;    //这里这一步注释的可以用来解释 s & tem 的含义int past = s - tem;int st = dp[past].time + a[i].cost - a[i].dead;//前一个状态所花的时间与当前状态时间上的一些处理if (st<0)   st = 0;//如果小于0是不是说明没有超过截止时间?那么我们就没有扣分,所以为0if (st + dp[past].score<dp[s].score)//这个在循环中更新,跳出第二重循环找到的就是最优解{dp[s].score = st + dp[past].score;dp[s].now = i;//当前节点dp[s].pre = past;//前一个状态的记录dp[s].time = dp[past].time + a[i].cost;//时间的更新}}}}stack<int> S;int tem = end - 1;cout << dp[tem].score << endl;//输出最少扣分while (tem){S.push(dp[tem].now);//利用栈先进后出的特点tem = dp[tem].pre;//为了迭代进行所以这样更新}while (!S.empty()){cout << a[S.top()].name << endl;S.pop();}}return 0;}

最后希望还是理解理解这种二进制表示的使用前提!