小题
一句话题意:给两个长为 \(n\) 的 \(double\) 数组 \(a\) , \(b\) ,定义收益: \(a\) 数组中选 \(x\) 个数放前面, \(b\) 数组中选 \(y\) 个数放前面,收益为 \(min(\sum_{i= 1}^{x} a_i, \sum_{i=1}^y b_i) - x - y\) 。求最大收益。
做法: \(TJ\) 说用三分做,但我只会贪心。我们把 \(a, b\) 都从大到小排序,分别求前缀和。显然的是我们从大到小选一定是最优的。首先我们想枚举 \(i,j\) ,表示 \(a ,b\) 数组分别选到第几个,算答案。但是就 \(\Theta(n^2)\) 了。我们发现,如果我选的 \(suma_i\) 刚好比 \(sumb_j\) 小(或等),那么如果我再把 \(b\) 数组往后选一个,肯定是在做无用功。因此我们只需考虑两边和相当的情况,即对每个位置在另一个数组中找lower_bound。一个细节是,要用总和更小的数组去找另一个。(玄学)
其实最开始是这样想的:从大到小排好序后,直接用两个指针扫两边,贪心找和更小的一边。这样做和上述做法本质一样,但不知道为什么 \(pt2\) 一直 \(WA\) ......
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
const double eps = 1e-6;
int n;
double a[N], b[N], sma[N], smb[N];
double res;
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%lf%lf", &a[i], &b[i]);
sort(a + 1, a + n + 1); sort(b + 1, b + n + 1);
for(int i = n; i; --i)
{
sma[n - i + 1] = sma[n - i] + a[i];
smb[n - i + 1] = smb[n - i] + b[i];
}
if(smb[n] < sma[n])
{
swap(a, b);
swap(sma, smb);
}
int p;
for(int i = 1; i <= n; ++i)
{
p = lower_bound(smb + 1, smb + n + 1, sma[i]) - smb;
res = max(res, max(sma[i] - i - p, smb[p - 1] - i - p + 1));
}
printf("%.4lf", res);
return 0;
}
//后一种WA点的做法
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n;
double a[N], b[N];
double res;
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%lf%lf", &a[i], &b[i]);
sort(a + 1, a + n + 1); sort(b + 1, b + n + 1);
int p1 = n, p2 = n;
double x = 0, y = 0;
while(p1 && p2)
{
if(x <= y)
x += a[p1--];
else
y += b[p2--];
--x; --y;
res = max(res, min(x, y));
}
printf("%.4lf", res);
return 0;
}

浙公网安备 33010602011771号