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