UVA11997_K_Smallest_Sums_多路并归_优先队列

本来今天早上才编过用优先队列模拟堆的一道题,结果晚上编又不会了- -,还得照着模版写- -

另外这个题我自己没想出来,去网上搜的解题报告看的

题意是给k组数每组有k个数,然后每组取一个,找前k个最小的和

然后下面是我在别人的博客里搜到的题解:

1.

转载于    z.arbitrary的博客 http://www.cnblogs.com/arbitrary/archive/2013/03/08/2950772.html

这题有简化版本的,即2个整数数组A,B,包含K个元素,在每个数组中取一个元素加起来,可以得到k^2个和,求这些和中最小的K个值。

我们需要把这k^2个和组织成如下k个有序表.

表1:A1+B1<=A1+B2<=......<=A1+Bk

表2: A2+B1<=A2+B2<=......<=A2+Bk

表k:Ak+B1<=AK+B2<=......<=Ak+Bk

我们可以用二元组(s,b)来表示一个元素即s=Aa+Bb;为什么不保存A的下标a呢?因为我们用不到a的值。如果我们需要元素(s,b)在表a的下一个元素(s',b+1).只需要计算s'=s+B[b+1]-B[b];

这样我们先将K个表的第一个元素压入优先队列,这样队列中就用k个元素了。然后从队列中出一个值,就压入这个值所在表的下一个元素,直到k个值都出了优先队列。这样就得到k个最小值了。。

然后对与K个数组。我们只需两两合并即可。

2.

转载于 hala_acmer 的博客 http://blog.sina.com.cn/s/blog_6f6e2aff0101cfse.html

我们找最小值其实是通过优先队列来找的


因为A和B已经排好序了  


B[0]肯定是B中最小值 我们往优先队列里push  k个数 分别是a[0]+b[0] 到 a[k-1]+b[0];

然后用优先队列弹出来k次 就得到了最小值

每次弹出来一个 加入是 node  那么就要加进去下一个 下一个的sum就要变成原来的减去那个b加下一个B

这样得到了两个数组的最小的k个数

那么k个数组呢 一个道理 先找出前两个数组的最小的k个数字

再用这个结果和第三个数组进行归并。。

最终得到答案了


以上是我主要借鉴的解题思路,然后就自己手写了,不过觉得代码风格特别烂- -

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#define N 800
using namespace std;
typedef struct MyStruct
{
	int bi;
	int val;
}NODE;

bool operator<(const NODE x,const NODE y)
{
	return x.val>y.val;
}
int k;
priority_queue<NODE,vector<NODE> >find_min;//用结构声明一个最小堆- -还是不会自己写

int a[N],b[N],ans[N];
void find_ans()
{
	int i;
	NODE temp,newd;
	while (!find_min.empty())
	{
		find_min.pop();
	}//每次求解前保证堆栈为空
	for ( i = 0; i < k; i++)
	{
		temp.val=a[i]+b[0];
		temp.bi=0;//注意,这里只需要存b的信息,因为当前a[i]的数值在val中存着,所以当取出栈顶元素时,应该压入的新的元素是val'=val+b[i+1]-b[i];
		find_min.push(temp);//把当前a[i]+b[0]先压入队列中
	}
	for ( i = 0; i < k; i++)
	{
		temp=find_min.top();//取出当前栈顶元素
		find_min.pop();
		ans[i]=temp.val;
		newd.bi=temp.bi+1;
		newd.val=temp.val+b[newd.bi]-b[temp.bi];//下一次要压入的元素就是val'=val+b[i+1]-b[i];
		find_min.push(newd);
	}
}
int main()
{
	int i,j;
	while (scanf("%d",&k)!=EOF)
	{
		
		for ( i = 0; i < k; i++)
		{
			scanf("%d",&a[i]);//输入第一行数			
		}
		sort(a,a+k);//每新输入一组数据就进行排序,保证所有的数组都是从小到大排
		for ( i = 0; i < k; i++)
		{
			scanf("%d",&b[i]);//第二行数
		}
		sort(b,b+k);
		find_ans();//求前两行的前k小的解
		for ( i = 2; i < k; i++)//并归
		{
			for ( j = 0; j < k; j++)
			{
				a[j]=ans[j];//把当前的答案存到a数组中
			}
			for ( j = 0; j < k; j++)
			{
				scanf("%d",&b[j]);//再输入新一组数据到b中
			}
			sort(b,b+k);
			find_ans();//再求当前解与新一组树的最优解
		}

		//输出
		for ( i = 0; i < k-1; i++)
		{
			printf("%d ",ans[i]);
		}
		printf("%d\n",ans[i]);
	}

	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值