Doing Homework HDU1074 【dp】

 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.

InputThe 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.
OutputFor 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


        
  
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门课程,每门作业只有做和没做两种状况,所以最多有(1<<15)-1种状态(若不清楚二进制运算符,可以看一下 http://www.cnblogs.com/lianchenboke/p/5061032.html )。我们直接枚举出每个状态,求其对应的最小值就行。我们寻找在当前状态下,上一状态的最小值,然后加上对应的这一门课程所扣的分数就行。最终状态就是dp[(1<<15)-1]。也就是找到当前所做科目下,找到每少做一门时的扣分加上这门的扣分,小于当前扣分就替换。而找少做一门时,我们应该从最后一门开始遍历,因为在扣分相同的情况下,我们要优先选择前面的科目。因为最后要找出科目顺序,所以我们要用一个数组来储存每个状态是做哪一门得来的。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define ll long long
#define ld long double
#define INF 0x3f3f3f3f
using namespace std;
char name[20][120];
int cost[20],dead_time[20],dp[1<<16],time[1<<16],def[1<<16];  //time用来储存每个状态对应的当前时间,dp为最少扣分,def为对应上一门科目
void Cout(int num) {                                       //输出最少状况下对应的科目顺序 
	if(num==0) return ;
	int i=def[num];
	Cout(num-(1<<i));
	cout<<name[def[num]]<<'\n';
}
int main() {
	std::ios::sync_with_stdio(false);
	int T;
	cin>>T;
	while(T--) {
		int n;
		cin>>n;
		for(int i=0; i<n; i++)
			cin>>name[i]>>dead_time[i]>>cost[i];
		dp[0]=0;
		for(int i=1; i<(1<<n); i++) {               //每个状态
			dp[i]=INF;
			for(int j=n-1; j>=0; j--) {         //先判断做没做这个科目,做了的话求不做它时的状态,进行对比,从后遍历
				int temp=1<<j;
				if((i&temp)==0) continue;
				int now_cost=time[i-temp]+cost[j]-dead_time[j];
				if(now_cost<0) now_cost=0;          //扣分没有负的
				if(dp[i]>dp[i-temp]+now_cost) {
					dp[i]=dp[i-temp]+now_cost;
					time[i]=time[i-temp]+cost[j];
					def[i]=j;
				}
			}
		}
		cout<<dp[(1<<n)-1]<<'\n';
		Cout((1<<n)-1);
	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章