NKOJ-3765 k个最小和

来源:互联网 发布:淘宝哪家近视眼镜店好 编辑:程序博客网 时间:2024/06/10 02:18

P3765k个最小和
时间限制 : - MS 空间限制 : 65536 KB
评测说明 : 时限1000ms
问题描述

有k个整数数组,各包含k个元素,从每个数组中选取一个元素加起来,可以得到k^k个和,求这些和中最小的k个值。

输入格式

第一行,一个整数k(k<=500)接下来k行,每行k个正整数(<=1000000)

输出格式

一行,k个有小到大排列的整数,表示最小的k个和

样例输入 1

3 1 8 5 9 2 5 10 7 6 

样例输出 1

9 10 12 

样例输入 2

2 1 1 1 2

样例输出 2

2 2

提示

样例1说明:选出的三组数分别是(1 2 6)  (1 2 7)  (1 5 6)

来源 改编自uva11997

no fuck to say

题解

其实这道题目没什么特别难的
我说一个词 优先队列
是不是感觉抓到了一点东西

详细解法

首先简化题目 假设只有两个数列

数列1   2 5 3 7 8 9数列2   5 8 6 4 7 5和题目中要求一样 求各取一个元素后的最小和解法十分简单排个序数列1  2 3 5 7 8 9数列2  4 5 5 6 7 8优先队列 存入数列1每一个数字加上数列2中最小数字的和即 加入 2+4  3+4  5+4  7+4  8+4  9+4这个时候毋庸置疑 2+4是最小的

接下来的操作十分重要

将 2+4取出 并加入 2+5 即加入 第一列第一个数字 + 第二列第一个数字

为什么这么做呢?

首先 你能保证当前队列中的最小值一定是最小的因为当前数列中的数字是数列1当中的数字 加上 数列2中最小的数字的和第二列没有数字能使这些和更小但是取出 2+4 之后 你需要做的是 使包含2的项继续存在于队列中对于 2 来讲 它的和的大小排列为2+4 2+5 2+5 2+6 2+7 2+8取出了2+4之后 就应该加入 2+5 因为这是次大的

解释的并不是很清楚 反正就是使数列1中的每一项都有一个最小和在单调队列中
你当前取出了它的最小和 就要加入它的次小和

所以说 单调队列中的项其实使 数列1中的每一个数字 加上当前它能够加上的最小的数列2中的数字 产生的当前的最小和
之所以要说使能够加上的 是因为之前已经加过的就不能加了

以此类推

将两行数列和中的前k个数存入 数组1 当中
然后将第三行的数字存入数组2

再次操作之后就得出了前三行数字相加得到的前k小值

于是在不停的重复之后 最后得到的就是k行和的最小值

附上对拍代码

#include <iostream>#include <cstdio>#include <algorithm>#include <queue>using namespace std;inline int input(){    char c=getchar();int o;    while(c>57||c<48)c=getchar();    for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;    return o;}int A[512],B[512],sum=0,k,pos;struct nums{    int n,p;    bool operator <(const nums& b)const    {        return n>b.n;    }}add;void remove(){    priority_queue<nums>st;add.p=1;    for(int i=1;i<=k;i++)add.n=A[i]+B[1],st.push(add);    A[1]=st.top().n;    add=st.top();st.pop();    add.p=2;add.n+=(B[2]-B[1]);    st.push(add);    for(int p=2;p<=k;p++)    {        add=st.top();st.pop();        A[p]=add.n;        add.n+=(B[add.p+1]-B[add.p]);add.p++;        if(add.p<=k)st.push(add);    }}int main(){    k=input();    for(int a=1;a<=k;a++)A[a]=input();    sort(A+1,A+k+1);    for(int a=2;a<=k;a++)    {        for(int b=1;b<=k;b++)B[b]=input();        sort(B+1,B+k+1);        remove();    }    for(int i=1;i<=k;i++)printf("%d ",A[i]);}
原创粉丝点击