题目描述
给定M个长度为n的序列,从每个序列中任取一个数求和,可以构成NM个和,求其中最小的N个和。N<=2000,M<=100。
输入
第一行是整数T,它显示测试用例的数量,然后是T个测试用例。每种情况的第一行都包含两个整数m,n。以下m行分别表示m序列。序列中没有整数大于10000。
输出
对于每个测试用例,按升序打印具有最小n个和的行,并用空格隔开。
样例输入
1
2 3
1 2 3
2 2 3
样例输出
3 3 4
解题思路:
1.我们可以先从简单情况考虑:当M=2时,就是从2个序列中任取一个数相加构成的N2个和中求出前N小的和。设这两个序列为A和B,将其分别排序。
2.可以发现最小和一定为A[1]+B[1],次小和就是A[1]+B[2]和A[2]+A[1]中取小值,即min(A[1]+B[2],A[2]+B[1]).若次小和为A[2]+B[1],那第三小的和就是A[1]+B[2],A[2]+B[2],A[3]+A[1]中最小的。总结一下,当确定A[i]+B[j]为第t小和时,A[i+1]+B[j]与A[i]+B[j+1]就是第t+1小和的备选方案
3.注意:A[1]+B[2]与A[2]+B[1]都可以产生A[2]+B[2]这个答案,会发生重复现象。可以这样规定:如果把j+1产生新的答案,那么以后只能增加j,不能增加i.也就是说,从A[1]+B[1]到任何A[i]+B[j]必须先向后移动i,再向后移动j,这样就可以使答案不重不漏,我们可以定义一个bool变量flag实现这一功能。
4.先建立一个小根堆,每个节点存储(i,j,flag)这几个参数,flag表示上一次移动的是否是j. 起初:(1,1,false) 然后取出堆顶(i,j,flag),然后把(i,j+1,true)插入堆,如果flag为false再把(i+1,j,false)也插入堆。 重复上一步N次,每次取出堆顶节点的权值A[i]+B[j]一起构成前N小和。时间复杂度为O(NlogN),整个算法时间复杂度为O(MNlogN)
代码:
#include<bits/stdc++.h>
#define N 10005
using namespace std;
struct node
{
int i,j,v,flag;
friend bool operator<(node a,node b)
{