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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章