编程之美挑战赛复赛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 }
原文地址:https://www.cnblogs.com/wuminye/p/3034642.html