0x10基本数据结构(0x17 二叉堆)例题2:序列

题意

题目链接

【题意】
给定m个序列,每个包含n个非负整数。
现在我们可以从每个序列中选择一个数字以形成具有m个整数的序列。
很明显,我们一共可以得到n^m个这种序列, 然后我们可以计算每个序列中的数字之和,并得到n^m个值。
现在请你求出这些序列和之中最小的n个值。 

 【输入格式】
第一行输入一个整数T,代表输入中包含测试用例的数量。
接下来输入T组测试用例。
对于每组测试用例,第一行输入两个整数m和n。
接下在m行输入m个整数序列,数列中的整数均不超过10000。 

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

 【数据范围】
0<m≤1000
0<n≤2000 

  【输入样例】
1
2 3
1 2 3
2 2 3 

  【输出样例】
3 3 4 

题解

这道题目,我们一开始看是蒙的。

但是看了YXC巨佬的视频不就会了吗。

这道题目,我们不能直接算 m m m个序列的 m m m小值。

我们发现其实我们可以算出第 1 1 1和第 2 2 2个序列第前 n n n小的序列和,然后把这 n n n项当成一个新的序列。

很容易想, 1 , 2 , 3 1,2,3 1,2,3的前 n n n小序列和其实就是 1 , 2 1,2 1,2的前 n n n小的序列然后和 3 3 3序列合并。(这个很好证)。

所以我们只要合并 m − 1 m-1 m1次序列成一个序列,然后这个序列就是答案。

那么对于两个序列我们如何算出其前 n n n小。

我们把第一个序列 a a a排序,然后看第二个序列 b b b,很明显,第 1 1 1小的肯定在 a 1 + b 1 , a 1 + b 2 , . . . a_{1}+b_{1},a_{1}+b_{2},... a1+b1,a1+b2,...中产生,那么我们就建个堆,把这个丢进去。

然后对于最小的 a 1 + b i a_{1}+b_{i} a1+bi,那么第二小就会在上面那坨除 a 1 + b i a_{1}+b_{i} a1+bi a 2 + b i a_{2}+b_{i} a2+bi中产生,再把 a 2 + b i a_{2}+b_{i} a2+bi丢进堆就行了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define  N  2100
using  namespace  std;
struct  node
{
	int  x,id/*表示是哪一行*/;
	node(int  xx,int  yy){x=xx;id=yy;}
};
inline  bool  operator<(node  x,node  y){return  x.x>y.x;}
priority_queue<node>ss;
int  a[N][N],n,m;
int  b[N],c[N],now[N];
void  merge(int  x1,int  x2)
{
	memcpy(b,a[x1],sizeof(b));memcpy(c,a[x2],sizeof(c));
	while(!ss.empty())ss.pop();
	sort(b+1,b+n+1);
	for(int  i=1;i<=n;i++)ss.push(node(b[1]+c[i],i)),now[i]=1;
	for(int  i=1;i<n;i++)
	{
		a[x2][i]=ss.top().x;int  x=ss.top().id;
		ss.pop();
		if(now[x]<n)ss.push(node(c[x]+b[++now[x]],x));
	}
	a[x2][n]=ss.top().x;
}
int  main()
{
	int  T;scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&m,&n);
		for(int  i=1;i<=m;i++)
		{
			for(int  j=1;j<=n;j++)scanf("%d",&a[i][j]);
		}
		if(m==1)
		{
			sort(a[1]+1,a[1]+n+1);
			for(int  i=1;i<n;i++)printf("%d ",a[m][i]);
			printf("%d\n",a[m][n]);
			continue;
		}
		for(int  i=2;i<=m;i++)merge(i-1,i);
		for(int  i=1;i<n;i++)printf("%d ",a[m][i]);
		printf("%d\n",a[m][n]);
	}
	return  0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值