小题

洛谷P4653

一句话题意:给两个长为 \(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;
}
posted @ 2022-04-09 15:41  Faker_yu  阅读(82)  评论(0)    收藏  举报