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