• P1631 序列合并


    题目大意

    有两个长度都是N的序列A和B,在A和B中各取一个数相加可以得到(N^2)个和,求这(N^2)个和中最小的N个。

    输入格式

    • 第一行一个正整数N;
    • 第二行有N个整数 表示(a_1)...(a_n) 且保证(a_i <= a_{i+1})
    • 第二行有N个整数 表示(b_1)...(b_n) 且保证(b_i <= b_{i+1})

    输出格式

    • 输出仅一行,包含N个整数,从小到大输出这N个最小的和,相邻数字之间用空格隔开。

    样例

    3
    2 6 6
    1 4 8
    
    3 6 7
    

    算法分析

    • 这个题不难 但是有一种很好的贪心思想值得我们学习
    • 首先看到这个题 暴力的思想很容易想出来 (N^2)暴力嘛 但是如何更快呢?
    • 首先我们想这样一个问题 如果我们要求的是最小的一个数 那怎么求呢? 没人会把所有的和都求出来然后sort吧 没错,最小的一个数肯定是(a_1) + (b_1)
    • 那么我们的前N大可不可以用同样的贪心思想求解呢???
      如果有这样一个表格表示a[i] + b[j]的和 那么就有
    (b_1) (b_2) (b_3)
    (a_1) 3 6 10
    (a_2) 7 10 14
    (a_3) 7 10 14
    • 通过这个表格显然会有一个很easy的规律:
      每个表格内的位置都比它右边和它下边的数要小
    • 这也很容易证明 对于它右边的数 因为(b_{j+1}) >= (b_j) 而加上同一个(a_i) 同理向下也是一样
    • 所以对于这个性质我们可以维护一个单调队列 首先把第一行的所有数扔进去 然后在里面找出最小值(显然所有数中的最小值就是第一个)
    • 然后将这个数下面的那个数加进队列(因为一开始是加入了一行)通过N次这样的取出与加入操作 可以求出前N小

    code

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5 + 10;
    int a[maxn],b[maxn];
    
    struct node{
    	int a,b,v;
    	node(int ta,int tb,int tv){
    		a = ta;
    		b = tb;
    		v = tv;
    	}
    	bool operator <(const node &A)const{
    		return v > A.v;
    	}
    };
    
    priority_queue<node> q;
    
    int main(){
    	int n;scanf("%d",&n);
    	for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
    	for(int i = 1;i <= n;++i)scanf("%d",&b[i]);
    	for(int i = 1;i <= n;++i){
    		q.push(node(i,1,a[i] + b[1]));
    	}
    	for(int i = 1;i <= n;++i){
    		int ta = q.top().a;
    		int tb = q.top().b;
    		int tv = q.top().v;
    		q.pop();
    		printf("%d ",tv);
    		q.push(node(ta,tb + 1,a[ta] + b[tb + 1]));
    	}
    	return 0;
    }
    
    如初见 与初见
  • 相关阅读:
    Java 异步编程
    对@repository,@Service, @Compent,@Controller注解的理解
    分布式锁的解决方案
    JVM垃圾收集器
    java死锁
    CountDownLatch和CylicBarrier以及Semaphare你使用过吗
    必懂知识——HashMap的实现原理
    重写equals为啥需要重写hashCode
    mysql数据库的索引
    mysql常见的优化策略
  • 原文地址:https://www.cnblogs.com/HISKrrr/p/13369922.html
Copyright © 2020-2023  润新知