POJ2442

题目: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;
}
原文地址:https://www.cnblogs.com/BakaCirno/p/11317022.html