【贪心+前缀】C. Fountains
http://codeforces.com/contest/799/problem/C
【题意】
有n做花园,有人有c个硬币,d个钻石 (2 ≤ n ≤ 100 000, 0 ≤ c, d ≤ 100 000) ,每一个花园用三个维度描述(a,b,c),分别是美丽度,所花钱币个数,钱币种类,当然,钱币之间不能兑换,该人必须要建筑两座花园,如果可以,输出两座花园总的美丽度,否则输出0;
【思路】
首先,有三种分类:
- 两座花园一座用钻石,一座用硬币
- 两座花园都用钻石
- 两座花园都用硬币
注意两座花园只能同时都买,不能只买一座
第一种情况比较简单,只要找出在两种分类中美丽度分别最大的即可
第二种情况和第三种是类似的,我们只考虑第二种:
首先,价格超过限度的直接省去,然后我们想知道价值和不超过限度的最大美丽度是多少。
我们可以枚举一座花园的价值price1,然后找到另一座满足price2<=c-price1的美丽度最大的花园。
一个很强的做法就是:
按price从小到大排序,for_max数组记录前缀0~i的最大美丽度,只要price[i]满足<=c-price1,就可以i++,最后找到的美丽度一定是最大的。
而且这样做只要一次for循环,price1从后向前枚举,price2的限界一定是增大的,所以只要不断在上一层循环的基础上增加就可以了~
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<cmath> 6 #include<vector> 7 #include<algorithm> 8 9 using namespace std; 10 const int maxn=1e5+5; 11 typedef pair<int,int> pii; 12 vector<pii> CC,DD; 13 int n,c,d; 14 15 int Solve(vector<pii> z,int money) 16 { 17 int for_max[maxn]; 18 int sz=z.size(); 19 sort(z.begin(),z.end()); 20 for_max[0]=0; 21 for(int i=1;i<=sz;i++) 22 { 23 for_max[i]=max(for_max[i-1],z[i-1].second); 24 } 25 int i=0; 26 int ans=0; 27 for(int k=sz-1;k>=0;k--) 28 { 29 while(i<k&&z[i].first+z[k].first<=money) 30 { 31 i++; 32 } 33 i=min(i,k); 34 if(i>0) 35 { 36 ans=max(ans,for_max[i]+z[k].second); 37 } 38 39 } 40 return ans; 41 } 42 int main() 43 { 44 scanf("%d%d%d",&n,&c,&d); 45 int p,b; 46 int max_c=0,max_d=0; 47 char tag[5]; 48 for(int i=0;i<n;i++) 49 { 50 scanf("%d%d%s",&b,&p,tag); 51 if(tag[0]=='C') 52 { 53 if(p>c) 54 { 55 continue; 56 } 57 CC.push_back(make_pair(p,b)); 58 max_c=max(max_c,b); 59 } 60 else 61 { 62 if(p>d) 63 { 64 continue; 65 } 66 DD.push_back(make_pair(p,b)); 67 max_d=max(max_d,b); 68 } 69 } 70 int ans; 71 if(max_c==0||max_d==0) 72 { 73 ans=0; 74 } 75 else 76 { 77 ans=max_c+max_d; 78 } 79 ans=max(ans,Solve(CC,c)); 80 ans=max(ans,Solve(DD,d)); 81 printf("%d\n",ans); 82 return 0; 83 }