zoukankan      html  css  js  c++  java
  • 【优先队列】【UVa11997】K Smallest Sums

    传送门

    Description

    Input

    Output

    Translation

    ·  给定k个长度为k的数组,把每个数组选一个元素加起来,这样共有kk种可能的答案,求最小的k个

    Sample Input

    3
    1 8 5
    9 2 5
    10 7 6
    2
    1 1
    1 2

    Sample Output

    9 10 12
    2 2

    Hint

    k<=750

    Solution

      显然可以一行一行做,同时如果S+now[j]最小,需要S最小。即我们只需要记录前i行的最小的k个ans,分别与当前行的k个相加,从k2个ans中选择前k个小的记录。显然前k小的可以开一个大根堆维护。即如果size>k则pop。这样一共有k次转移,每次转移有k2个状态,维护k的堆的复杂度为Logk,这样总复杂度为O(k3logk)

      易于通过数学归纳证明取第i行的最小的只需要与前i-1行最小的k个ans相加得到。这样我们不妨用数组ans记录前i行最小的k个ans,现在我们考虑我们有两个数组a,b,需要取k个ans最小。不妨设a和b都是有序的,我们显然有如下关系:

      a1+b1≤a1+b2≤a1+b3≤a1+b4≤……≤a1+bk

      a2+b1≤a2+b2≤a2+b3≤a2+b4≤……≤a2+bk

      ……

      ak+b1≤ak+b2≤ak+b3≤ak+b4≤……≤ak+bk

    这样显然ai+bj可能成为前k小的ans当且仅当ai+bj-1是前k小的ans。

    我们维护一个可能成为ans的序列,每次取这个序列中最小,显然最小值可以成为合法的ans。同时不妨设这个值为ai+bj,那么我们将ai+bj+1压入序列,因为它可能成为合法的ans。

    初始化上,因为ai+b1可能成为合法的ans,我们将这k个全部压入队列中。

    Code

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxn 1010
    
    inline void qr(int &x) {
        char ch=getchar(),lst=NULL;
        while(ch>'9'||ch<'0') lst=ch,ch=getchar();
        while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        if(lst=='-') x=-x;
    }
    
    template <typename T>
    inline T mmax(const T &a,const T &b) {if(a>b) return a;return b;}
    template <typename T>
    inline T mmin(const T &a,const T &b) {if(a<b) return a;return b;}
    template <typename T>
    inline T mabs(const T &a) {if(a>=0) return a;return -a;}
    
    template <typename T>
    inline void mswap(T &a,T &b) {T temp=a;a=b;b=temp;}
    
    int k,MU[maxn],ans[maxn];
    
    struct Zay {
        int v,s;
        Zay(int a=0,int b=0) {v=a;s=b;}
        inline bool operator <(const Zay &others) const{return this->v>others.v;}
    };
    
    std::priority_queue<Zay>Q;
    void clear();
    
    int main() {
        while(~scanf("%d",&k)) {
            clear();
            for(int i=1;i<=k;++i) qr(ans[i]);
            for(int i=2;i<=k;++i) {
                memset(MU,0,sizeof MU);
                for(int j=1;j<=k;++j) {
                    qr(MU[j]);
                }
                std::sort(MU+1,MU+1+k);
                while(!Q.empty()) Q.pop();
                for(int j=1;j<=k;++j) {
                    Q.push(Zay(ans[j]+MU[1],1));
                }
                for(int j=1;j<=k;++j) {
                    Zay temp=Q.top();Q.pop();
                    ans[j]=temp.v;
                    if(temp.s<k) Q.push(Zay(temp.v-MU[temp.s]+MU[temp.s+1],temp.s+1));
                }
            }
            for(int i=1;i<k;++i) printf("%d ",ans[i]);printf("%d
    ",ans[k]);
        }
        return 0;
    }
    
    void clear() {
        memset(ans,0,sizeof ans);
    }

    Summary

    在堆的应用中,维护一坨可能合法的解进行操作,是一种常用的思路。比如本题和dijkstra算法都是这个思路。这一坨合法的解一般满足下面两个条件:

    第一,这坨解中最大/小的解一定是合法的解。比如本题中,序列中最小的一定是合法的解,dijkstra算法中,堆中权值和最小的解一定是到该点的最短路等等

    第二,通过寻找合法解,可以获得其他可能解。比如在本题中,ai+bj合法时,ai+bj+1是可能解。在dijkstra算法中,找到到一个点的最短路可以更新和它相邻的点的可能最短路压入堆中。

  • 相关阅读:
    滚轮选择器效果 封装类
    我的第一个博客——Fragment遇到的问题
    LeetCode#11 Container With Most Water
    LeetCode#5 Longest Palindromic Substring
    LeetCode#3 Longest Substring Without Repeating Characters
    邮件协议
    六、应用层——文件传输协议FTP
    六、应用层——DNS
    平衡二叉树
    二叉树的性质
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/9399148.html
Copyright © 2011-2022 走看看