POJ2442:Sequence

浅谈堆:https://www.cnblogs.com/AKMer/p/10284629.html

题目传送门:http://poj.org/problem?id=2442

我们先简化题意,假设只有两行。

那么显然,最小值是\(a_1+b_1\)。并且次小值集合是\(a_2+b_1,a_1+b_2\)

假设\(a_1+b_2\)是次小值,那么次次小值集合就是\(a_2+b_1,a_2+b_2,a_1+b_3\)

也就是说,当\(a_i+b_j\)成为当前最小值之后,\(a_{i+1}+b_j,a_i+b_{j+1}\)将会进入当前集合的次小值集合。我们用堆维护一下,每次取出最小值再把扩展出来的两个数扔回去就行了。

但是,\(a_2+b_2\)显然是可以通过\(a_1+b_2\)\(a_2+b_1\)扩展出来的,如果不去重的话显然状态是非常非常多的,那样空间时间都没法保证。

所以我们强行勒令\(a_i+b_j\)只能转移到\(a_i+b_{j+1}\),前提是\(a_i+b_j\)是由\(a_i+b_{j-1}\)扩展来的。

也就是说,假设现在有两个指针分别在第一行和第二行上移动,对于\(a_i+b_j\)必须满足\(i\)先移动\(j\)再移动然后到了这个地方。这样就不会有重复的状态了,也不会露掉中间值。

时间复杂度:\(O(mnlogn)\)

空间复杂度:\(O(n)\)

代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn=2e3+5;

int n,m;
int num1[maxn],num2[maxn],tmp[maxn];

int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}

struct node {
    bool bo;
    int x,y;

    node() {}

    node(int _x,int _y,bool _bo) {
        x=_x,y=_y,bo=_bo;
    }

    bool operator<(const node &a)const {
        return num1[x]+num2[y]<num1[a.x]+num2[a.y];
    }
};

struct Heap {
    int tot;
    node tree[maxn];

    void ins(node v) {
        tree[++tot]=v;
        int pos=tot;
        while(pos>1) {
            if(tree[pos]<tree[pos>>1])
                swap(tree[pos],tree[pos>>1]),pos>>=1;
            else break;
        }
    }

    node pop() {
        node res=tree[1];
        tree[1]=tree[tot--];
        int pos=1,son=2;
        while(son<=tot) {
            if(son<tot&&tree[son|1]<tree[son])son|=1;
            if(tree[son]<tree[pos])
                swap(tree[son],tree[pos]),pos=son,son=pos<<1;
            else break;
        }
        return res;
    }
}T;

int main() {
    int TEST=read();
    while(TEST--) {
        m=read(),n=read();
        for(int i=1;i<=n;i++)
            num1[i]=read();
        sort(num1+1,num1+n+1);
        for(int i=1;i<m;i++) {
            for(int j=1;j<=n;j++)
                num2[j]=read();
            sort(num2+1,num2+n+1);T.tot=0;
            int cnt=0;T.ins(node(1,1,0));
            for(int j=1;j<=n;j++) {
                node res=T.pop();
                tmp[++cnt]=num1[res.x]+num2[res.y];
                T.ins(node(res.x,res.y+1,1));
                if(!res.bo)T.ins(node(res.x+1,res.y,0));
            }
            for(int j=1;j<=n;j++)
                num1[j]=tmp[j];
        }
        for(int i=1;i<=n;i++)
            printf("%d ",num1[i]);
        puts("");
    }
    return 0;
}

转载于:https://www.cnblogs.com/AKMer/p/10288345.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值