UVA 11997 K Smallest Sums(优先队列)
题意:
给你一个整数K,并且给你K组数,每组K个数,现在在每组中任取一个数,然后相加可以得到一个"和",这样的和共有K^K个.要你输出所有可能和值中最小的那K个。
分析:
刘汝佳:训练指南P189例题.
问题1:如果只有A,B,C三个大小为K的数组,我们如何求"和"能获得最小的前K个和呢?
我们只需要将A和B数组求出前K小的和(第K+1小到之后的所有和值我们都不用管,因为后面压根用到这些值),然后用这个和数组去同样与C数组求出前K小的和即可。如果有K个这样的数组,我们依然用A1与A2求前K小的和,然后用和数组与A3再求和,然后用结果再继续同样与A4求和,与A5求和...等即可。
问题2:A和B两个数组求前K小的和,如何计算能快速得到解呢?
穷举法要K^2的复杂度.这里我们用优先队列来解.因为原本有K^2个可能的和结果。
假设A与B中的数已经排好了序(从小到大),那么前K小的数肯定从下面K个队列中出来(我们等同于将K^2个和分成了K个队列,且每个队列中的元素都是从小到大排列):
队列1: A[1]+B[1] , A[1]+B[2] , … , A[1]+B[K]
队列2: A[2]+B[1] , A[2]+B[2] , … , A[2]+B[K]
队列3: A[3]+B[1] , A[3]+B[2] , … , A[3]+B[K]
队列4: A[4]+B[1] , A[4]+B[2] , … , A[4]+B[K]
...
队列4: A[K]+B[1] , A[K]+B[2] , … , A[K]+B[K]
且每个队列中队首元素肯定是当前最小的候选和之一.所以我们每次从上述K个队列中的K个队首元素里找出最小的那个作为一个前K小的和(每次操作复杂度为logK),连续K次操作可以得到所有前K小的和。
(注意:程序实现只用了一个优先队列priority_queue,此队列中保存了上述K个队列的K个对首元素)
所以我们只需要K^logK复杂度即可求出2个数组组合的前K小数.
综上所述,我们只需要一次构建两个数组的前K小数即可求得最小的前K小数了.
AC代码(新):
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 750+5;
int A[maxn][maxn];
//Node用于构建优先队列的元素
struct Node
{
int sum;//和
int id;//B数组元素下标
Node(int sum,int id):sum(sum),id(id){}
bool operator<(const Node &rhs)const
{
return sum>rhs.sum;
}
};
//将A与B数组的前n小和存入C数组中
void merge(int *A,int *B,int *C,int n)
{
priority_queue<Node> Q;
for(int i=0;i<n;i++)
Q.push(Node(A[i]+B[0],0));
for(int i=0;i<n;i++)
{
Node tmp=Q.top(); Q.pop();
C[i]=tmp.sum;
if(tmp.id+1<n) Q.push(Node(tmp.sum-B[tmp.id]+B[tmp.id+1], tmp.id+1));
}
}
int main()
{
int k;
while(scanf("%d",&k)==1)
{
for(int i=0;i<k;i++)
{
for(int j=0;j<k;j++)
scanf("%d",&A[i][j]);
sort(A[i],A[i]+k);
}
for(int i=1;i<k;i++)
merge(A[0],A[i],A[0],k);
printf("%d",A[0][0]);
for(int i=1;i<k;i++)
printf(" %d",A[0][i]);
printf("\n");
}
return 0;
}
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=1000;
int a[maxn],b[maxn];
int k;
struct node
{
int va;//A数组的值
int id;//B数组的元素序号
bool operator<(const node&rs)const
{
return va+b[id]>rs.va+b[rs.id];
}
};
void merge(int *a,int *b,int *c)
{
priority_queue<node> pq;
for(int i=0;i<k;i++)
{
node r;
r.va=a[i];
r.id=0;
pq.push(r);
}
for(int i=0;i<k;i++)
{
node r=pq.top();pq.pop();
c[i]=r.va+b[r.id];
if(r.id<k-1)
{
r.id++;
pq.push(r);
}
}
}
int main()
{
while(scanf("%d",&k)==1)
{
for(int i=0;i<k;i++)
scanf("%d",&a[i]);
sort(a,a+k);
for(int i=1;i<k;i++)
{
for(int j=0;j<k;j++)
scanf("%d",&b[j]);
sort(b,b+k);
merge(a,b,a);
}
printf("%d",a[0]);
for(int i=1;i<k;i++)
printf(" %d",a[i]);
printf("\n");
}
return 0;
}