Acwing - 146. 序列

 

                                                                                         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 ;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值