• 编程之美挑战赛复赛B题


    【题目大意】

    Alice新开了一家公司,它的下面有两个项目,分别需要N1和N2个人来完成。现在有N个人前来应聘,于是Alice通过面试来决定他们中的哪些人会被录用。

    Alice在面试中,会仔细考察他们能如何为公司的项目带来收益。她给每个人打了两个分值Q1和Q2,表示他加入第一个和第二项目分别能带来的收益值。同时,她也会仔细考察他们每个人的缺点,并且给每人打了另两个分值C1和C2,表示他们进入每个项目可能带来的负面效应。Alice心目中的最优决策是,在决定好录用哪些人以及每个人在哪个项目下工作之后,他们为公司带来的收益总和,除以他们为项目带来的负面效应总和,这个比值要最大。你能帮他计算出在最优决策下,这个比值为多少吗?

    前来应聘的人数总是大于等于两个项目需求人数的总和,因此Alice一定会恰好招N1+N2个人,分配给第一个项目N1个人,分配给第二个项目N2个人,没有人会同时属于两个项目。

    【分析】

             题目要求的是 SUM(Q)/SUM(C) 的最大值,假设最大值为ans , 则在最优情况下  SUM(Q)/SUM(C)=ans  ===>   SUM(Q)=ans*SUM(C)=SUM(ans*C)
                 ==>  SUM(Q)-SUM(ans*C)=0  ==>  SUM(Q-ans*C)=0
             看来关键就在于  SUM(Q-ans*C) 了,如果 SUM(Q-ans*C)>0 则说明 ans 还可以在一定的限度内增加,使相减后的和再减小趋近于0。由于和最小为0,即使 Qi-ans*Ci 都小于0,也不会出现 SUM(Q-ans*C)<0 的情况。对于某一固定的 ans ,SUM(Q-ans*C) 的值也会应为人员的选取不同而不同,但是可以肯定的是只要确定某种选择方案,SUM(Q-ans*C) 的值一定随 ans 的增大而减小;根据前面的说明,要使 ans 尽可能增大,对于每个 ans 的SUM(Q-ans*C)就要尽可能大,这里就要用到动态规划了。

            dp[i][j]  (j<=i) 表示前 i 个人,其中 j个人到项目 2 中,不多于 i-j 个人到项目1中。p[i]=pair(Q1-ans*C1,Q2-ans*C2)
                dp[i][j]=max{dp[i-1][j]+(i-j<=n1)*p[i].first,dp[i-1][j-1]+p[i].second}    ( j<i && j<=n2)
           边界:   dp[i][0]=dp[i-1][0]+(i<=n1)*p[i].first   (i个人全进项目1,乘(i<=n1)是为了看项目1如否还可以加人)
                dp[i][i] = dp[i-1][i-1]+p[i].second;     (i个人全进项目2,必须满足条件 (i<=n2) ,可以加入项目2)

                       SUM(Q-ans*C) 的最大值为 max{ dp[j][n2] }  (j>=n1+n2, j<=n)

            由于 ans 对 SUM(Q-ans*C) 的影响的单调的 (SUM(Q-ans*C) 总是用DP求出尽可能大的值),ans 越大,SUM(Q-ans*C)的最大值 就会减少 。所以要做的就是二分 ans ,如果 SUM(Q-ans*C) 大于 0,可以提高下限,如果 SUM(Q-ans*C) 等于0,并不能说明 ans 达到最优解,有可能是超过最优解,所以要减小上限,最优解定在上下限内;这样多做几次二分(有限次数),就可以夹逼求出 ans 的最优解。

       

         附上别人的代码,我也是看这代码有所启发的。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstdlib>
     4 #include <cmath>
     5 #include <map>
     6 #include <string>
     7 #include <cstring>
     8 #include <sstream>
     9 #include <algorithm>
    10 
    11 using namespace std;
    12 
    13 const int MAXN = 505;
    14 
    15 int n, n1, n2;
    16 int q1[MAXN], c1[MAXN], q2[MAXN], c2[MAXN];
    17 pair<double, double> a[MAXN];
    18 double f[MAXN][MAXN];
    19 bool ok(double ans)
    20 {
    21     for (int i = 1; i <= n; ++i) {
    22         a[i] = make_pair(q1[i] - ans * c1[i], q2[i] - ans * c2[i]);
    23     }
    24     sort(a + 1, a + n + 1);
    25     reverse(a + 1, a + n + 1);
    26     double res = 0;
    27     f[0][0] = 0;
    28     for (int i = 1; i <= n; ++i) {
    29         f[i][0] = f[i - 1][0] + (i <= n1) * a[i].first;
    30         for (int j = 1; j < i && j <= n2; ++j) {
    31             f[i][j] = max(f[i - 1][j] + (i - j <= n1) * a[i].first, f[i - 1][j - 1] + a[i].second);
    32         }
    33         if (i <= n2) f[i][i] = f[i - 1][i - 1] + a[i].second;
    34     }
    35     for (int i = n1 + n2; i <= n; ++i)
    36         res = max(res, f[i][n2]);
    37     return res > 0;
    38 }
    39 void work()    
    40 {
    41     scanf("%d%d%d", &n, &n1, &n2);
    42     for (int i = 1; i <= n; ++i) {
    43         scanf("%d%d%d%d", &q1[i], &c1[i], &q2[i], &c2[i]);
    44     }
    45     double f = 0, r = 20000;
    46     for (int tmp = 0; tmp < 70; ++tmp) {
    47         double mid = (f + r) / 2;
    48         if (ok(mid)) f = mid;
    49             else r = mid;
    50     }
    51     printf("%.6f\n", (f+r)/2);
    52 }
    53 int main()
    54 {
    55     int cases;
    56     scanf("%d", &cases);
    57     for (int tcase = 1; tcase <= cases; ++tcase) {
    58         printf("Case #%d: ", tcase);
    59         work();
    60     }
    61     return 0;
    62 }
  • 相关阅读:
    《Java架构师的第一性原理》12计算机基础之计算机网络
    《Java架构师的第一性原理》11计算机基础之操作系统
    《Java架构师的第一性原理》34分布式计算之分布式消息队列(AcitveMQ、RabbitMQ、RocketMQ、Kafka、ZeroMQ)
    《Java架构师的第一性原理》32分布式计算之分布式缓存第1篇Redis
    《Java架构师的第一性原理》41存储之MySQL深入浅出全网最全,没有之一
    《Java架构师的第一性原理》41存储之MySQL第11篇数据库调优
    C++拷贝赋值operator=
    C++浅拷贝与深拷贝
    C++左值和右值
    C++函数的哑元参数
  • 原文地址:https://www.cnblogs.com/wuminye/p/3034642.html
Copyright © 2020-2023  润新知