题意:有k行数,每行k个数,从每行中抽出一个数,相加得到sum, 求这样的sum的前k小数,并输出。
分析:大白书(刘汝佳 著)优先队列章节,
假设现在有两行A,B(从小到大排序),求前k小,则:
A1+B1<=A1+B2<=....A1+Bn
A2+B1<=A2+B2<=....A2+Bn
A3+B1<=A3+B2<=....A3+Bn
...........
用一个优先队列来维护前k小的值,先把(A1+B1),(A2+B1),(A3+B1)...放入队列,但这k个数不一定是前k小,唯一可以确定的是(A1+B1)是第一小的,
A1+B1肯定是最小的,则出队并保存。此时队首元素是(A2+B1),但不一定是次小值,次小值为min(A2+B1,A1+B2),于是,就把(A1+B2)压入队列,队首元素就为次小值。以此类推,每次将队首元素出队,并保存,然后压入该元素的右边元素到队列,如队首为Aa+Bb,出队->保存->将(Aa+B(b+1))入队。这样就能依次找到前k小的值。
那么k行咋办呢?其实可以把A,B数组求出的前k小值保存到A数组,然后加入下一个数组,以相同的方式,可以求出k行的最小前k个和。
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <string.h>
#include <map>
#include <set>
using namespace std;
int mat[751][751];
class node
{
public:
int s,b;
node(int ss,int bb):s(ss),b(bb){}//s=Aa+Bb,b存的是Bb 的下标
bool operator <(const node& x)const
{
return s>x.s;
}
};
int main()
{
int k,i,j;
while(cin>>k)
{
for(i=1;i<=k;i++)
{
for(j=1;j<=k;j++)
{
cin>>mat[i][j];
}
sort(mat[i]+1,mat[i]+1+k);
}
for(i=2;i<=k;i++)
{
priority_queue<node>q;
for(j=1;j<=k;j++)
{
q.push(node(mat[1][j]+mat[i][1],1));
}
for(j=1;j<=k;j++)
{
node tx=q.top();
q.pop();
mat[1][j]=tx.s;
if(tx.b+1<=k)
q.push(node(tx.s-mat[i][tx.b]+mat[i][tx.b+1],tx.b+1));
//队首为s=Aa+Bb,入队的下一个元素为s'=s-Bb+B(b+1)=Aa+B(b+1);
}
}
for(j=1;j<=k;j++)
{
if(j!=1)cout<<" ";
cout<<mat[1][j];
}
cout<<endl;
}
return 0;
}