题目:http://poj.org/problem?id=2442
题意:给m个包含n个数字的数列,在每个数列中选一个数加起来,这样的和由nm个,求这些和中最小的前n个。
可以先看两个数列的情况,假设这两个数列为A,B。先将这两个数列从小到大排序,那么最小的和肯定是A[1]+B[1],而第二小的就是min(A[2]+B[1],A[1]+B[2]),假设就是A[2]+B[1]吧。那么第三小的就是min(A[2]+B[1],A[3]+B[1],A[1]+B[2]),根据这个规律,我们知道,如果确定A[i]+B[j]是前n个中的一个那么就可以将A[i]+B[j+1]和A[i+1]+B[j]纳入下一个最小值的考虑范围之内。那么我们用优先队列就可以动态的取我们考虑范围内的最小值,从而得出这两个数列前n小和。
如果扩展到m个数列的话,我们可以先求出前2个数列的前n小和,然后利用这个结果和第3个数列,求出前3个数列的前n小和,以此类推,求出m个数列的前n小和。
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #include <queue> #define MAXN 2010 using namespace std; int T,n,m,num1[MAXN],num2[MAXN],num3[MAXN],vis[MAXN][MAXN]; struct Node{ int i,j; friend bool operator < (const Node &a,const Node &b) { return num1[a.i]+num2[a.j]>num1[b.i]+num2[b.j]; } }; priority_queue<Node> pq; int main() { scanf("%d",&T); while(T--) { scanf("%d %d",&m,&n); for(int i=1;i<=n;i++) scanf("%d",&num3[i]); sort(num3+1,num3+n+1); for(int i=2;i<=m;i++) { memset(vis,0,sizeof(vis)); for(int j=1;j<=n;j++) scanf("%d",&num2[j]); sort(num2+1,num2+n+1); for(int j=1;j<=n;j++) num1[j]=num3[j]; while(!pq.empty()) pq.pop(); pq.push({1,1}); for(int j=1;j<=n;j++) { int p1=pq.top().i,p2=pq.top().j; pq.pop(); num3[j]=num1[p1]+num2[p2]; if(p1<n&&!vis[p1+1][p2]) pq.push({p1+1,p2}),vis[p1+1][p2]=1; if(p2<n&&!vis[p1][p2+1]) pq.push({p1,p2+1}),vis[p1][p2+1]=1; } } for(int i=1;i<=n;i++) printf("%d ",num3[i]); printf(" "); } return 0; }