NKOJ-3765 k个最小和

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 
#include 
#include 
#include 
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_queuest;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]);
}

你可能感兴趣的:(NKOI,数据结构)