HDU-1074.DoingHomework(撞鸭dp二进制压缩版)

之前做过一道二进制压缩的题目,感觉也不是很难吧,但是由于见少识窄,这道题一看就知道是撞鸭dp,却总是无从下手....最后看了一眼博客,才顿悟,本次做这道题的作用知识让自己更多的认识二进制压缩,并无其它卵用......呜呜呜~~~

  本题大意:到期末了,某同学的n位老师给他布置了n门家庭作业,要求他在布置作业后的D天之内完成,并且已知它每门作业所需的天数C,每门作业超时一天就要扣一分,让你求出要如何合理安排做作业的顺序才能使他扣掉的总分最少......还需要输出做作业的顺序和最优情况下扣的分数......

  本题思路:因为要得到所有做题的顺序就有n!种这么多,那么此时我们看到n  <= 15,那就能考虑到二进制撞鸭了,二进制撞鸭之前发过一个博客有讲过,这里再强调一下比如0101表示第1和3科的作业做了,第二科作业没做,那么我们可以枚举做作业先后的所有的情况,然后取最优即可,这只是思路,那么具体我们要如何分析这个问题呢

下面我详细讲解......

  首先思考一个问题:我们二进制枚举所有的写作先后过程,那么我们的dp要保存什么状态呢,第一我们需要先找出最小分数即score,需要回溯保存一个过程的上一个过程pre,需要每次写作业之后更新已经过去的时间time,需要保存当前正在写作业的下标ind,就ok了,对于dp[ i ],我们在所有科目中逆序寻找(为什么是逆序寻找呢....因为我们需要利用一个数组存做作业的先后顺序,也就是利用一个pre存它之前完成的状态的编号,遍历输出时顺序也是反的,为了保证字典序输出,我们只能逆着遍历,~~~~(>_<)~~~~呜呜呜~~~),如果发现i状态下如果已经完成了作业j,那么我们就将作业j分离出来在这一步单独再次完成,在这一步利用dp[i - 1<<j]将dp[ i ]的值更新并找到最小值(由于在完成三项任务1, 2, 3时肯定要完成对应的前两项1,2 || 2,3 || 1,3),那么我们的最优值就是由这个思路得来的,由于i - 1<<j 恒小于i,所以它在i之前求得,我们就可以对dp[ i ]的值进行更新,那么对于dp[ i ],我们就可以写出他的状态转移方程dp[ i ] = max(dp(i - 1 << j) + dp[ j ])(j 为状态i 的一个子问题)。

  参考代码:

 1 #include <iostream>
 2 #include <cstring>
 3 #include <string>
 4 #include <stack>
 5 using namespace std;
 6 
 7 const int maxn = 15 + 2, INF = 0x3f3f3f3f;
 8 int t, n;
 9 struct node {
10     string name;
11     int deadline, cost;
12 } a[maxn];
13 struct node1 {
14     int pre, time, score, ind;
15 } dp[1 << 15];
16 
17 int main () {
18     ios::sync_with_stdio(false);
19     cin >> t;
20     while(t --) {
21         cin >> n;
22         for(int i = 0; i < n; i ++)
23             cin >> a[i].name >> a[i].deadline >> a[i].cost;
24         memset(dp, 0, sizeof dp);
25         int nbit = 1 << n;
26         for(int i = 1; i < nbit; i ++) {
27             dp[i].score = INF;
28             for(int j = n - 1; j >= 0; j --) {
29                 int temp = 1 << j;
30                 if(!(temp & i)) continue;
31                 int tem = i - temp;
32                 int t = dp[tem].time + a[j].cost - a[j].deadline;
33                 t = t > 0 ? t : 0;
34                 if(t + dp[tem].score < dp[i].score) {
35                     dp[i].score = t + dp[tem].score;
36                     dp[i].pre = tem;
37                     dp[i].ind = j;
38                     dp[i].time = dp[tem].time + a[j].cost;
39                 }
40             }
41         }
42         cout << dp[nbit - 1].score << endl;
43         stack<int> q;
44         int t = nbit - 1;
45         while(dp[t].time) {
46             q.push(dp[t].ind);
47             t = dp[t].pre;
48         }
49         while(!q.empty()) {
50             int k = q.top();
51             cout << a[k].name << endl;
52             q.pop();
53         }
54     }    
55     return 0;
56 }
View Code
原文地址:https://www.cnblogs.com/bianjunting/p/10627380.html