kuangbin专题十二 HDU1074 Doing Homework (状压dp)

本文介绍了一种使用状态压缩动态规划(状压DP)的方法来解决如何合理安排作业顺序以最小化扣分的问题。通过将作业的不同状态表示为二进制数,实现了对所有可能状态的有效遍历。

Doing Homework

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


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

题目大意:给出一系列的作业,还有每一门作业的截止日期和做完需要的时间,老师规定每超过一天,就要扣一分,让你求一个做作业的顺序,使最后扣分最少。如果扣分相同,输出字典序最小的序列。

思路:本来以为只是贪心,但是发现没有解释的过的策略。然后搜了题解,发现是状压dp,然后就放了几天,今天终于想通了。

注释详细的博客:https://blog.youkuaiyun.com/xingyeyongheng/article/details/21742341

让我有思路的博客:https://blog.youkuaiyun.com/libin56842/article/details/24316493

如果看不懂,可以先看一下我的另一篇博客。看完之后,再看这道题应该就懂了。

-                                  --------------->>>戳这里<<<------------   

 

 

 

 

dp[i] 表示达到状态i的最少扣分

首先,全排列所有作业,肯定有一组是满足要求的,但是n!很大。所以想到二进制表示一系列的状态。当然二进制并不明显看出来,这里是  1<<n, 用 1~1<<n的具体数字,它的二进制就是一系列作业的状态,1表示做了,0表示没做。枚举 1~1<<n 的所有状态,枚举 i 属于 0~n-1 temp = (1 << i),枚举二进制的某一位是1, 如果 s & temp != 0 那么上一状态就是 s-temp说明状态s-temp可以到达s。然后最后的 (1<<n)-1 也就是全是1(全做)的score即可

(详情见上面我的另一篇博客)

 

 

(给出两种输出代码)详情见上面dalao的博客。

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <math.h>
 4 #include <string.h>
 5 #include <stdlib.h>
 6 #include <string>
 7 #include <vector>
 8 #include <set>
 9 #include <map>
10 #include <queue>
11 #include <algorithm>
12 #include <sstream>
13 #include <stack>
14 using namespace std;
15 #define mem(a,b) memset((a),(b),sizeof(a))
16 #define mp make_pair
17 #define pb push_back
18 #define fi first
19 #define se second
20 #define sz(x) (int)x.size()
21 #define all(x) x.begin(),x.end()
22 #define forn(i, x, n) for(int i = (x); i < n; i++)
23 #define nfor(i, x, n) for(int i = x-1; i >= n; i--)
24 typedef long long ll;
25 const int inf = 0x3f3f3f3f;
26 const ll INF =0x3f3f3f3f3f3f3f3f;
27 const double pi = acos(-1.0);
28 const double eps = 1e-5;
29 const ll mod = 1e9+7;
30 
31 struct node{
32     string name;
33     int end, cost;
34 }stu[20];//初始的 
35 
36 struct Node{
37     int time, now, pre, score;
38 }dp[1<<15]; 
39 
40 int main() {
41     int _, n;
42     for(scanf("%d", &_);_;_--) {
43         scanf("%d", &n);
44         forn(i, 0, n) {
45             cin >> stu[i].name >> stu[i].end >> stu[i].cost;
46         }
47         forn(s, 1, (1<<n)) {//全排列所有状态 
48             dp[s].score = inf;//刚开始全是正无穷 
49             nfor(i, n, 0) {
50                 int temp = 1 << i;
51                 if(!(s & temp)) continue;//不能由做完i到达s  
52                 int past = s - temp;//如果能 past就是做完j 就到达s的状态 
53                 int st = dp[past].time + stu[i].cost - stu[i].end;//进行计算。减少的分数 
54                 if(st < 0)//小于0,就不用减 
55                     st = 0;
56                 if(dp[s].score > dp[past].score + st) {//更新 
57                     dp[s].score = dp[past].score + st;
58                     dp[s].now = i;//为了输出 
59                     dp[s].pre = past;
60                     dp[s].time = dp[past].time + stu[i].cost;
61                 }
62             }
63         }
64         stack<int> S;//用栈维护输出顺序 
65         int pos = (1<<n)-1;
66         cout << dp[pos].score << endl;
67         while(pos) {
68             S.push(dp[pos].now);
69             pos = dp[pos].pre;
70         }
71         while(!S.empty()) {
72             cout << stu[S.top()].name << endl;
73             S.pop();
74         }
75     }
76 }

 

 


 

 1 const int MAX=(1<<15)+10;
 2 int n;
 3 int dp[MAX],t[MAX],pre[MAX],dea[20],fin[20];//dp[i]记录到达状态i扣的最少分,t时相应的花去多少天了
 4 char s[20][110];
 5 
 6 void output(int x){
 7     if(!x)return;
 8     output(x-(1<<pre[x]));
 9     printf("%s\n",s[pre[x]]);
10 }
11 
12 if(dp[i]>dp[i-temp]+score){
13     dp[i]=dp[i-temp]+score;
14     t[i]=t[i-temp]+fin[j];//到达状态i花费的时间
15     pre[i]=j;//到达状态i的前驱,为了最后输出完成作业的顺序 
16 } 

 

 

转载于:https://www.cnblogs.com/ACMerszl/p/9572932.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值