zoukankan      html  css  js  c++  java
  • 算法设计与分析 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)
    	b12肯定有一者是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
    ", res);
    	return 0;
    }
    
    
  • 相关阅读:
    Oracle 获取本周、本月、本季、本年的第一天和最后一天(转载)
    easyui tabs页签显示在底部属性
    java mybatis XML文件中大于号小于号转义(转载)
    原生JS日历 + JS格式化时间格式
    ajax之async属性
    Easyui 行编辑
    css中实现显示和隐藏(转)
    layer弹出层 获取index
    js中关于json常用的内容、js将数字保留两位小数
    发布项目到github上web服务器来运行
  • 原文地址:https://www.cnblogs.com/yejifeng/p/12078873.html
Copyright © 2011-2022 走看看