146. 序列
给定m个序列,每个包含n个非负整数。
现在我们可以从每个序列中选择一个数字以形成具有m个整数的序列。
很明显,我们一共可以得到n^m个这种序列, 然后我们可以计算每个序列中的数字之和,并得到n个值。
现在请你求出这些序列和之中最小的n个值。
输入格式
第一行输入一个整数T,代表输入中包含测试用例的数量。
接下来输入T组测试用例。
对于每组测试用例,第一行输入两个整数m和n。
接下在m行输入m个整数序列,数列中的整数均不超过10000。
输出格式
对于每组测试用例,均以递增顺序输出最小的n个序列和,数值之间用空格隔开。
每组输出占一行。
数据范围
0<m≤10000<m≤1000,
0<n≤20000<n≤2000
输入样例:
1
2 3
1 2 3
2 2 3
输出样例:
3 3 4
题解 : 先考虑M= 2 时的情况,
这里有两个序列 , 假设已经按照从小到大的顺序排好序了 。
a[1] , a[2] ,a[3] , ... a[n] .
b[1] , b[2] ,b[3] . ....b[n] .
首先最小的和一定是 a[1] + b[1] , 其次是min{ (a[1]+b[2] ) ,(a[2]+b[1]) } , 假设是 a[1] + b[2] , 那么再其次就是 min { a[2]+b[1] ,
a[2] +b[2 ] , a[3] + b[1] } .a[2+b[1] 一定 小于 a[2]+b[2] 。也就是 min {a[2] + b[2] , a[1]+b[3] } 也就是说当确定 a[i] +b[j] 为第 k 小和后 , (a[i+1]+b[j]) , (a[i]+b[j+1] ) 就加入到 第k+1 小和的备选答案 ,
假设有两个指针分别指向 a[i] 和 b[j] , 每次把其中一个指针向后移一次就可能产生下一个和 .
建立一个小跟堆(每次弹出最小的), 堆中每个节点存储一个 a[i]+b[j] , 和指针 , 静态指针指向a[i] ,
M = 2 时 ,
1 . 首先先从第一个序列中的第一个数(最小的) , 分别和第二个序列的数做和,加入到堆中
2 . 每次 弹出最小的和, 作为一个前n个最小和,循环 n 次就求出前n 个最小和 .
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std ;
const int MAX = 2005 ;
//priority_queue<node> q ;
typedef pair<int,int> PII ;
int a[MAX] ;
int b[MAX] ;
int c[MAX] ;
struct node {
int v ;
int flag ;
};
int n , m ;
void work() {
priority_queue<PII ,vector<PII> ,greater<PII> > q ;
for(int i = 1 ; i<=n ; i++ ) q.push({a[1]+b[i],1}) ;
for(int i = 1 ; i<=n ; i++ ) {
PII t = q.top() ; // 每次弹出来最小的和
q.pop() ;
c[i] = t.first;// 第 i 小和
q.push({t.first+a[t.second+1]-a[t.second],t.second+1}) ;
// q.push ( {a1 +b1 +a2 - a1} , 2 } // second 表示指向a数组的下一个元素指针
// a1+b1 +a2 -a1
// 原来是 a1+b1 , 现在更新成 b1+a2 ,
// b1 +a2 +a3 -a2
// 在下一次 更新成 b1+a3..
}
// 更新 a 数组
memcpy(a+1,c+1,sizeof(int)*n) ;
}
int main(){
int t ;
cin >> t ;
while(t--){
cin >> m >> n ;
for(int i = 1 ; i<=n ; i++ ) cin >>a[i] ;
sort(a+1,a+n+1) ;
for(int i = 1 ;i<m ; i++) {
for(int j = 1 ; j<=n ;j++ ) {
cin >> b[j] ;
}
sort(b+1,b+n+1) ;
work() ;
}
for(int i = 1 ;i<=n ; i++ ){
cout<<a[i]<<" ";
}
cout<<endl;
}
return 0 ;
}