zoukankan      html  css  js  c++  java
  • (DP)最大价值事件序列选取

    题目:

    有 T (小于10000)件事情,每件事件有一个开始时间(从1开始)一个结束时间(小于 10000),和一个正整数价值 。 

    求做这些事件所能得到的最大的价值 。

    思考:

    将事件由事件开始先后顺序排序,先开始的排在前面,后开始的排在后面。按这个顺序事件号记录在一个数组order中。

    初始化数组 dp 为 0 , dp[w] 表示 选取了 结束时间为 w 的某事件后 所做事件的总价值。

    初始化变量dpmax = 0 记录dp值的最大值。

    设 end 为 第 i 件 事结束的时间。

    做第 i 件事时,搜索前面做过的 i - 1 件事,找到dp值最大的。设 seek 为 搜索到的dp值最大的事件序号。加上 第 i 件事的价值,转移状态到 dp[end]= max( dp[end] , dp[seek]+ Val[i])

    TIP:由于dp[i] 表示的并不是 1 - i 时间内最大的价值。所以从i - 1处转移并不可靠。而记录的dpmax是全局最大,也就是说,比 事件 i 先开始的事件,不一定结束时间比 事件 i 早。因此也不能单纯的使用 dpmax。只能向前搜索。搜索一个以前做过的事件,且 dp[该事件] 值是最大,继续转移。

    代码:

     1 //
     2 // T 个事件,给出开始时间 结束时间 价值 。 求做这些事件所能获得的最大的价值 。
     3 // O(n^2) 算法 
     4 //
     5 #include <iostream>
     6 #include <stdio.h>
     7 #include <string.h>
     8 #include <algorithm>
     9 
    10 #define MAX 10010
    11 using namespace std;
    12 int B[MAX]; // 事件的开始时间。
    13 int E[MAX]; // 时间的结束时间。
    14 int V[MAX]; // 事件的价值。
    15 int order[MAX]; // 按照起始时间排序所获得的做事件的顺序。
    16 int dp[MAX]; // 做事做到某个时间点所能获得的最大价值。
    17 int dpmax; // 记录当前dp数组中最大的价值。
    18 ////////////////////////////////
    19 ///////////////////////////////
    20 void init()
    21 {
    22     memset(dp , 0 ,MAX * sizeof(int));
    23     dpmax = 0;
    24     return ;
    25 }
    26 bool CMP(int a , int b)
    27 {
    28     if(B[a] > B[b]) return 0;
    29     return 1;
    30 }
    31 int main()
    32 {
    33     int T;
    34     while(~scanf("%d",&T))
    35     {
    36         init();
    37         for(int i = 0 ; i < T ; i++) 
    38         {
    39             cin >> B[i] >> E[i] >> V[i];
    40             order[i] = i;
    41         }
    42         sort(order , order + T , CMP);
    43         //////////////////////////////////////////
    44         for(int i = 0 ; i < T ; i++)
    45         {
    46             int seek = 0;
    47             int begin = B[i];
    48             int end = E[i];
    49             int val = V[i];
    50             for(int j = i ; j >= 0 ; j--) 
    51             {
    52                 if(E[j] > begin) continue;
    53                 if(dp[E[j]] > dp[seek])
    54                 {
    55                     seek = E[j];
    56                 }
    57              }
    58              dp[end] = max(dp[end] , dp[seek] + V[i]);
    59             dpmax = max(dp[end],dpmax);
    60         }
    61         cout << dpmax << endl;
    62     }
    63     return 0;
    64 }

    综上可得复杂为 O(n^2) 不够好。

    是否可以降低复杂度 ?

    可以,可以定义dp[w] 为时间点 w 以前 做事件所能得到的最大价值。这样就可以每次查找第 i 件事前的第一个dp不为0的值,进行状态转移。

    如何更新 dp 数组 ?设第 i 件事结束时间为 end , 向前搜索 第一个 dp 值不为 0 的值 。 保留大的数,储存在 dp[end] 中 。

    但是这样不一定能降低很多的时间。

    最坏情况是 :

      第 n 件事 开始 时间点 是 第 n 小 , 结束时间 是 第 n 大 。

    ( 第一件事 的开始时间最大,结束时间最晚)

    (第二件事 的开始时间第二大,结束时间倒数第二晚)

    (。。。。。。。。。。。。。。。。。。。。)

    这样平摊下去,仍然是 n ^ 2.

  • 相关阅读:
    从O(n^3) 到 O(n)求最大连续和
    冲刺博客NO.6
    冲刺博客NO.5
    冲刺博客NO.4
    冲刺博客NO.3
    冲刺博客NO.2
    冲刺博客NO.1
    结对开发:四则运算(网页版)
    课堂作业3
    软件工程概论02~ 四则运算
  • 原文地址:https://www.cnblogs.com/ticsmtc/p/4984711.html
Copyright © 2011-2022 走看看