算法设计与分析 4.4 洪尼玛与魔法卡
★题目描述
洪尼玛有2n张魔法卡,每张魔法卡上都有两个正整数a、b。
洪尼玛准备将这些魔法卡平均分成2堆,每堆有n张魔法卡。
一堆魔法卡所能发挥的功力值为该堆魔法卡中最小的a值与最小的b值的乘积。
洪尼玛有一种X能力,可以使某张魔法卡上的a、b值互换,并且可以无限次使用这个能力。
洪尼玛想知道将这些魔法卡平均分成2堆后(可以使用X能力),这2堆魔法卡一共能发挥的最大功力值是多少?
★输入格式
接下来2n行,每行两个正整数ai、bi,表示每张魔法卡上的两个值;
对于60%的数据,1<=n<=103;
对于100%的数据,1<=n<=105、1<=ai、bi<=109。
★输出格式
输出一个正整数,表示最大功力值。
★样例输入
2
1 2
3 4
5 6
7 8
★样例输出
32
★提示
无
★参考代码
思路参考共享文件
/*
假设最大功力值为 a1*b1 + a2*b2
1.为了减少考虑的情况,在接受输入时让每张卡片上a统一小于b
2.按照a值对所有卡片升序排列
3.因为题目要求两堆卡片数量都为n,所以可确定:
第一堆最小a值(即a1)为a边最小的值
第二堆最小a值最大化(即a2最大)只可能为a边第n+1小的值
注意:第二堆的a值不能直接定为a边第n+1小的值,因为你考虑下面一种情况,有4张卡
2 10
4 100
6 11
8 110
那么最佳的应该是 2*10 + 4*100
所以可以确定寻找a2应该枚举,遍历第2张到第n+1张卡片,然后看那种组合能得到最大res
4.在确定a1,a2情况下要如何确定左右两堆的最小b值(即b1、b2)
b1\b2肯定有一者是b边的最小值,所以这两种情况都要尝试
思路:
可以确定的是a1为a边最小值,然后:
第一种情况,b边最小值在第一堆,那么b1为b边最小值,然后枚举a2,最大化b2
第二种情况,b边最小值在第二堆,那么b2为b边最小值,然后枚举a2,最大化b1
for k in [2,n+1] //因为已经按a排好序,在前k张放在第1堆,第k张放在第2堆的情况下
a2 = C[k].a //第k张的a值即为第二堆的最小值
if b1为b边最小值,那么最大化b2
从剩下的 [k+1, 2*n] 张卡片中找出b边尽可能大的n-1张卡放在第二堆
b2 = min(C[k].b, 剩下第k+1到2*n张卡片中b边第n-1大的值)
if b2为b边最小值,那么最大化b1
从剩下的 [k+1, 2*n] 张卡片中找出b边尽可能大的n-k+1张卡放在第一堆
b1 = min(第[1,k-1]张卡的b边最小值, 从剩余的第[k+1,2*n]张卡选出b边第n-k+1大的值)
*/
#include<bits/stdc++.h>
using namespace std;
int n;
struct Card{
int a,b;
bool operator < (const Card Y) const{return a==Y.a ? b<Y.b : a<Y.a;}
}C[200000+5];
int min_b=0x7FFFFFFF, pre_min_b[100000+5]={0x7FFFFFFF}; //记录从1到n的前缀最小值
int main(){
scanf("%d",&n);
for(int i=1; i<=2*n; ++i){
scanf("%d%d",&C[i].a, &C[i].b);
if(C[i].a>C[i].b) swap(C[i].a, C[i].b);
min_b = min(min_b, C[i].b); //b边最小值
}
sort(C+1, C+2*n+1);
int a1 = C[1].a; //a边最小值为a1
for(int i=1; i<=n; ++i) pre_min_b[i]=min(pre_min_b[i-1], C[i].b);//排序后的,b边前缀最小
priority_queue<int> q1; //降序优先队列,用于最大化b1,长度在变小
priority_queue<int, vector<int>, greater<int> > q2; //升序优先队列,用于最大化b2,长度固定为n-1
for(int i=n+2; i<=2*n; ++i) q1.push(C[i].b),q2.push(C[i].b);
int res=0, a2, b1, b2;
for(int k=n+1; k>1; --k){ //前k-1张卡片放在第1堆中,第k张卡片放在第2堆中
a2 = C[k].a;
//b1为b边最小值,最大化b2
b1 = min_b;
b2 = min(C[k].b, q2.top());
res = max(res, a1*b1+a2*b2);
//b2为b边最小值,最大化b1
b2 = min_b;
if(k==n+1) b1 = pre_min_b[n];
else {
b1 = min(pre_min_b[k-1], q1.top());
q1.pop();
}
res = max(res, a1*b1+a2*b2);
q1.push(C[k].b);
q2.push(C[k].b);
if(q2.size()>=n) q2.pop();
}
printf("%d\n", res);
return 0;
}