Poj P2442 Sequence___最小堆

45 篇文章 0 订阅
13 篇文章 0 订阅

题目大意:

多组数据,每组给出 M M 个序列,每个序列有N个元素,从每个序列中任选一个数求和,显然有 NM N M 种和,求最小的 N N 个序列和分别是多少。

0<m<=100,0<n<=2000

分析:

用优先队列动态维护前 N N 小的序列和,
因为对优先队列的运用不熟练,所以写的比较生疏,
大致就是,设我们已经做完了前i个序列,长度为 i i 的序列的前N的序列和为数组 f[] f [ ] ,即 f[1 f [ 1 ~ N] N ] ,那么显然,
现将 f[] f [ ] 升序排列,
对于第 i+1 i + 1 个序列,
我们序列长度为 i+1 i + 1 的序列和最小值,显然就是 f[1]+a[i+1][1] f [ 1 ] + a [ i + 1 ] [ 1 ]
那么次小值,可能就是 f[2]+a[i+1][1],f[1]+a[i+1][2] f [ 2 ] + a [ i + 1 ] [ 1 ] , f [ 1 ] + a [ i + 1 ] [ 2 ] ,我们一直这样找然后用优先队列维护一个最小堆即可,
有一个优化,对于 f[1]+a[2]f[2]+a[1] f [ 1 ] + a [ 2 ] 跟 f [ 2 ] + a [ 1 ] ,显然都能到 f[2]+a[2] f [ 2 ] + a [ 2 ] ,这里的重复可以优化掉。

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define N 2005

using namespace std;

struct Node 
{
    int id1, id2, num1, num2;
    bool flag;
    friend bool operator < (Node aa, Node bb) 
    {
         return (aa.num1 + aa.num2) > (bb.num1 + bb.num2);
    }
    Node(){};
    Node(int a, int b, int c, int d, bool e)
    {
         id1 = a, id2 = b, num1 = c, num2 = d, flag = e;
    }
}orz;
priority_queue <Node> Q;
int a[N], f[N], n, m;

void Merge() 
{
     while (Q.size()) Q.pop();
     int C[N], cnt = 0;
     Q.push(Node(1, 1, f[1], a[1], 0));
     while (Q.size() && cnt < m)
     {
            orz = Q.top();
            Q.pop();
            C[++cnt] = orz.num1 + orz.num2;
            if (orz.id1 < m && !orz.flag) 
                Q.push(Node(orz.id1 + 1, orz.id2, f[orz.id1 + 1], a[orz.id2], 0));
            if (orz.id2 < m) 
                Q.push(Node(orz.id1, orz.id2 + 1, f[orz.id1], a[orz.id2 + 1], 1));
    }
     for (int i = 1; i <= m; i++) f[i] = C[i];
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T--) 
    {
           scanf("%d %d", &n, &m);
           for (int i = 1; i <= m; i++) scanf("%d", &f[i]);
           sort(f + 1, f + m + 1);

           for (int i = 2; i <= n; i++) 
           {
                for (int j = 1; j <= m; j++) scanf("%d", &a[j]);
                sort(a + 1, a + m + 1);
                Merge();
           }
           for (int i = 1; i <= m; i++) printf("%d ", f[i]);
           printf("\n");        
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值