优先队列 - Sequence - POJ 2442

优先队列 - Sequence - POJ 2442

给定m个序列,每个包含n个非负整数。

现在我们可以从每个序列中选择一个数字以形成具有m个整数的序列。

很明显,我们一共可以得到nm个这种序列, 然后我们可以计算每个序列中的数字之和,并得到nm个值。

现在请你求出这些序列和之中最小的n个值。

输入格式

第一行输入一个整数T,代表输入中包含测试用例的数量。

接下来输入T组测试用例。

对于每组测试用例,第一行输入两个整数m和n。

接下在m行输入m个整数序列,数列中的整数均不超过10000。

输出格式

对于每组测试用例,均以递增顺序输出最小的n个序列和,数值之间用空格隔开。

每组输出占一行。

数据范围
0 < m ≤ 1000 , 0 < n ≤ 2000 0<m≤1000, 0<n≤2000 0<m1000,0<n2000

输入样例:

1
2 3
1 2 3
2 2 3

输出样例:

3 3 4

分析:

我 们 要 从 m 个 长 度 为 n 的 序 列 中 各 挑 选 m 个 数 得 到 新 序 列 再 求 和 , 再 从 和 的 序 列 中 挑 选 前 n 小 的 数 , 我们要从m个长度为n的序列中各挑选m个数得到新序列再求和,再从和的序列中挑选前n小的数, mnmn

等 价 于 先 求 和 , 再 从 m 个 和 的 序 列 中 挑 选 n 个 数 组 合 成 新 的 序 列 , 输 出 新 序 列 前 n 小 的 数 。 等价于先求和,再从m个和的序列中挑选n个数组合成新的序列,输出新序列前n小的数。 mnn

而 合 并 m 个 序 列 , 可 以 两 两 合 并 m − 1 次 , 每 次 两 两 合 并 产 生 n 2 个 数 , 保 留 前 n 小 的 数 再 继 续 合 并 。 而合并m个序列,可以两两合并m-1次,每次两两合并产生n^2个数,保留前n小的数再继续合并。 mm1n2n

暴 力 合 并 共 操 作 ( m − 1 ) n 2 次 , 考 虑 优 化 合 并 方 式 。 暴力合并共操作(m-1)n^2次,考虑优化合并方式。 (m1)n2

考 虑 合 并 序 列 : a 1 , a 2 , . . . , a n 和 b 1 , b 2 , . . . , b n 考虑合并序列:\\a_1,a_2,...,a_n和b_1,b_2,...,b_n :a1,a2,...,anb1,b2,...,bn

我 们 首 先 将 序 列 a 从 小 到 大 排 序 , 接 着 将 两 个 序 列 组 合 而 成 的 n 2 个 数 分 组 : 我们首先将序列a从小到大排序,接着将两个序列组合而成的n^2个数分组: an2

1 ) 、 a 1 + b 1 , a 2 + b 1 , . . . , a n + b 1 2 ) 、 a 1 + b 2 , a 2 + b 2 , . . . , a n + b 2 . . . n ) 、 a 1 + b n , a 2 + b n , . . . , a n + b n 1)、a_1+b_1,a_2+b_1,...,a_n+b_1\\2)、a_1+b_2,a_2+b_2,...,a_n+b_2\\...\\n)、a_1+b_n,a_2+b_n,...,a_n+b_n 1)a1+b1,a2+b1,...,an+b12)a1+b2,a2+b2,...,an+b2...n)a1+bn,a2+bn,...,an+bn

因 为 序 列 a 以 按 从 小 到 大 顺 序 排 好 , 所 以 每 一 组 的 第 一 个 数 就 是 该 组 的 最 小 值 , 我 们 先 挑 出 n 组 的 最 小 值 : 因为序列a以按从小到大顺序排好,所以每一组的第一个数就是该组的最小值,我们先挑出n组的最小值: an

1 ) 、 a 1 + b 1 2 ) 、 a 1 + b 2 . . . k ) 、 a 1 + b k . . . n ) 、 a 1 + b n 1)、a_1+b_1\\2)、a_1+b_2\\...\\k)、a_1+b_k\\...\\n)、a_1+b_n 1)a1+b12)a1+b2...k)a1+bk...n)a1+bn

那 么 这 n 2 个 数 中 的 最 小 的 数 一 定 在 这 n 个 数 中 , 我 们 假 设 a 1 + b k 是 最 小 的 , 1 ≤ k ≤ n 。 那么这n^2个数中的最小的数一定在这n个数中,我们假设a_1+b_k是最小的,1≤k≤n。 n2na1+bk1kn

挑 出 a 1 + b k 后 , 第 k 组 的 最 小 值 就 变 成 了 a 2 + b k 。 挑出a_1+b_k后,第k组的最小值就变成了a_2+b_k。 a1+bkka2+bk

于 是 n 组 中 的 最 小 值 变 成 了 于是n组中的最小值变成了 n

1 ) 、 a 1 + b 1 2 ) 、 a 1 + b 2 . . . k ) 、 a 2 + b k . . . n ) 、 a 1 + b n 1)、a_1+b_1\\2)、a_1+b_2\\...\\k)、a_2+b_k\\...\\n)、a_1+b_n 1)a1+b12)a1+b2...k)a2+bk...n)a1+bn

这 样 , 继 续 从 以 上 n 个 数 中 挑 选 最 小 的 数 , 就 得 到 了 这 n 2 个 数 中 的 次 小 数 。 这样,继续从以上n个数中挑选最小的数,就得到了这n^2个数中的次小数。 nn2

以 此 类 推 , 重 复 进 行 n 次 操 作 即 可 。 以此类推,重复进行n次操作即可。 n

这 个 过 程 可 以 用 小 根 堆 来 维 护 , 时 间 复 杂 度 O ( n l o g n ) 。 这个过程可以用小根堆来维护,时间复杂度O(nlogn)。 O(nlogn)

共 合 并 m − 1 次 , 总 的 时 间 复 杂 度 为 O ( m n l o g n ) 。 共合并m-1次,总的时间复杂度为O(mnlogn)。 m1O(mnlogn)

具体落实:

小 根 堆 以 两 个 序 列 的 和 为 第 一 关 键 字 , 由 于 挑 选 某 个 数 后 , 要 将 其 后 的 数 加 入 堆 中 , 因 此 还 需 存 储 对 应 的 下 标 。 小根堆以两个序列的和为第一关键字,由于挑选某个数后,要将其后的数加入堆中,因此还需存储对应的下标。

用 p a i r 来 存 储 。 用pair来存储。 pair

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>

#define P pair<int,int>
#define x first
#define y second

using namespace std;

const int N=2010;

int n,m,T;
int a[N],b[N],c[N];

void merge()   //将序列a和b组合后挑出前n小的数存储到a中
{
    priority_queue<P,vector<P>,greater<P> > heap;
    for(int i=0;i<m;i++) heap.push({a[0]+b[i],0});
    
    for(int i=0;i<m;i++)
    {
        P t=heap.top();
        heap.pop();
        int s=t.x,p=t.y;
        c[i]=s;
        heap.push({s-a[p]+a[p+1],p+1});
    }
    
    for(int i=0;i<m;i++) a[i]=c[i];
}


int main()
{
    cin>>T;
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++) scanf("%d",&a[i]);
        
        sort(a,a+m);
        
        for(int i=0;i<n-1;i++)
        {
            for(int j=0;j<m;j++) scanf("%d",&b[j]);
            merge();
        }
            
        for(int i=0;i<m;i++) printf("%d ",a[i]);
        puts("");
    }
    
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值