算法设计与分析 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;
}

猜你喜欢

转载自www.cnblogs.com/yejifeng/p/12078873.html
4.4