题目链接: Fountains
题目大意:
n座喷泉, 每座喷泉有三个属性, 1. 建造所需要的材料(硬币或钻石), 2. b:美丽值, 3. p:建造所需材料数量, 一个喷泉只用一种材料
你有c个硬币, d个钻石, 建造两座喷泉, 求美丽值最大为多少, 如果无法建造两座喷泉, 输出0
解题思路
分三类情况讨论
1. 一座使用硬币, 一座使用钻石, 直接分别遍历两个集合(硬币建的和钻石建的喷泉), 求出硬币和钻石喷泉在c个硬币和d个钻石下能建造出美丽值的最大值
2. 两座都使用硬币
3. 两座都使用钻石
后两种情况解决思路:
1. 将使用硬币的所有喷泉按照建造花费从小到大排序
2. 在排序的基础上, 预处理mx[j] := 喷泉[0, j]里最大美丽值
3. 遍历所有喷泉(用硬币建造的), 设当前遍历到的喷泉j的花费为p, 美丽值为b, 那么建完这座剩下x个材料;
4. 那么建造的另一座喷泉最大美丽值就是花费小于等于x的所有喷泉中的最大美丽值;
5. 因为喷泉已经排序, 我们可以二分查找x的上界(第一个花费大于x的位置)pos, 那么区间[0, pos-1]里所有喷泉花费小于等于x;
6. 那么mx[pos-1]就是另一座喷泉的最大美丽值
7. (为了防止出现同一座喷泉用了两次的情况, 我们设第一座编号在排序后的数组中编号大于第二座, 二分查找时只在[0, j-1]范围内查找, 这样就不用考虑重复用的情况了)
因为两类做法完全一样, 那么干脆用长度为2的数组记录两类组喷泉的数据, 可以用循环减少代码量
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1E5 + 10;
struct P
{
int b, p;
P(int a=0, int b=0) :b(a), p(b) {}
bool operator<(const P & x) const { return p < x.p; }
}c[2][MAXN];//0为硬币喷泉, 1为钻石
int cnt[2], n, m[2], mx[2][MAXN];
int main()
{
scanf("%d%d%d", &n, &m[0], &m[1]);//m[0]m[1]为硬币, 钻石数量
for(int i=0; i<n; ++i)
{
int a, b; char ch;
scanf("%d %d %c", &a, &b, &ch);
if(ch == 'C') c[0][cnt[0]++] = P(a, b);
else c[1][cnt[1]++] = P(a, b);
}
int ans = 0, M[2] = {0, 0 };//两组各自的最大美丽值
for(int i=0; i<2; ++i)
for(int j=0; j<cnt[i]; ++j)
if(m[i] >= c[i][j].p) M[i] = max(M[i], c[i][j].b);
if(M[0] && M[1]) ans = M[0] + M[1];
for(int i=0; i<2; ++i)
{
sort(c[i], c[i]+cnt[i]);
mx[i][0] = c[i][0].b;
for(int j=1; j<cnt[i]; ++j) mx[i][j] = max(mx[i][j-1], c[i][j].b);
for(int j=1; j<cnt[i]; ++j)
{
int x = m[i] - c[i][j].p;//造完第一座还剩的材料
int pos = upper_bound(c[i], c[i]+j, P(0, x)) - c[i];//区间[0, pos-1]所有喷泉建造花费小于等于x
if(pos) ans = max(ans, c[i][j].b+mx[i][pos-1]);//mx[i][pos-1], 第二座最大美丽值
}
}
cout << ans << endl;
return 0;
}