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 <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]);
}
發佈了79 篇原創文章 · 獲贊 15 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章