HDU 1074 Doing Homework (dp+状态压缩+路径记录)

来源:互联网 发布:美工全职 编辑:程序博客网 时间:2024/06/15 14:42

传送门:点击打开链接

参考链接:点击打开链接

Doing Homework

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 9864    Accepted Submission(s): 4716


Problem Description
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
23Computer 3 3English 20 1Math 3 23Computer 3 3English 6 3Math 6 3
 

Sample Output
2ComputerMathEnglish3ComputerEnglishMath
Hint
In the second test case, both Computer->English->Math and Computer->Math->English leads to reduce 3 points, but the word "English" appears earlier than the word "Math", so we choose the first order. That is so-called alphabet order.
 


题目说给你n门科目,每科都有做完需要花费的时间跟做不完的罚时,一天只能做一科,只要当天不做其他的就罚时,问罚时如何最小。

给出了最多15门课程,这15门课程都有一个特点,要么做,要么不做,正好对应二进制的0,1.如果想枚举这15科的每一种状态,用数组或者单纯的枚举实现起来很麻烦,此时就用到了状态压缩,强大的位运算,将1到15这几个数转换成二进制的15位,每一位要么是0,要么是1,这样就能组成一个2^15的数,从0到2^15代表着每一种状态。枚举每一种状态,再去枚举每一科的科目,去判断这门科目在状态内或不在状态内的罚时,记录下最小罚时,再去枚举下一门科目,
这里要注意,题目输出路径时,相等的话需要输出输入时的顺序,我们记录路径得到的结果都是反着的,需要逆序输出,此时不妨逆序枚举每一科,这样逆序输出时,输出的顺序就对应着输入的顺序。具体看代码实现,

代码实现:

#include<iostream>#include<algorithm>#include<cstring>#include<cmath>#include<queue>#include<cstdio>#define ll long long#define mset(a,x) memset(a,x,sizeof(a))using namespace std;const double PI=acos(-1);const int inf=0x3f3f3f3f;const double esp=1e-12;const int maxn=(1<<15)+5;const int mod=1e9+7;int dp[maxn],pre[maxn],cost[20],cut[20],trans[maxn]; //dp[i]表示前i种状态中扣分最小值,pre记录路径,cut记录花费,cost记录减分,trans记录当前分数 string name[20];void display(int x)          //递归输出 {if(x==0)                 //等于0时输出结束 return ;display(x^(1<<pre[x]));      cout<<name[pre[x]]<<endl;}int main(){int i,j,k,t,n;cin>>t;while(t--){cin>>n;for(i=0;i<n;i++)cin>>name[i]>>cut[i]>>cost[i];mset(pre,0);mset(trans,0);int bit=1<<n;                   //bit代表总共有多少种状态 for(i=1;i<bit;i++)              //枚举每一种状态 {dp[i]=inf;                  //每种状态下的dp都设为最大值 for(j=n-1;j>=0;j--)         //逆序枚举作业 {int temp=1<<j,temp1;    //temp表示当前状态含有状态j if(!(i&temp))           //当前状态等于前一状态 continue;temp=i^temp;            //没做j之前的状态 temp1=trans[temp]+cost[j]-cut[j];   //此时的得分情况 if(temp1<0)             //得分更新temp1 temp1=0;if(dp[temp]+temp1<dp[i])//加入了j状态后加上目前扣分小于枚举的状态 {dp[i]=dp[temp]+temp1;           //记录 trans[i]=trans[temp]+cost[j];   //记录目前得分情况 pre[i]=j;                       //记录前一个的编号 } }}cout<<dp[bit-1]<<endl;                     //输出 display(bit-1);}return 0;}


原创粉丝点击