本篇是刘汝佳《算法竞赛入门经典——训练指南》的读书笔记。
知识点: 优先队列
解题思路:
先考虑(2)个包含(k)个元素的数组的情况:在每个数组中取一个数加起来,可以得到(2^k)个和,求这些和中最小的(k)个值。做法是先将两个数组从小到大排好序,然后先取数组(A)的最小值与数组(B)的各个值相加,放进一个从小到大排列的优先队列中,接下来每次取出优先队列的队首元素(即目前的最小值),在此基础上更新这个值(即把这个值中数组(B)的值稍微增大一点),让新值入队,取出(k)个最小的即可结束。
那么对于(k)个包含(k)个元素的数组又该怎么做呢?易知上述的操作能把两个数组合并成一个,则只需进行(k-1)次操作,将(k)个数组两两合并为一组即可。
AC代码:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 const int maxn = 770; 5 6 int inp[maxn][maxn]; 7 int ans[maxn]; 8 struct Item{ 9 int sum,b; 10 bool operator <(const Item &rhs)const{ 11 return sum>rhs.sum; 12 } 13 }; 14 int main() 15 { 16 priority_queue<Item> q; 17 int k; 18 while(scanf("%d",&k)==1){ 19 for(int i=0;i<k;i++){ 20 for(int j=0;j<k;j++) scanf("%d",&inp[i][j]); 21 sort(inp[i],inp[i]+k); 22 } 23 for(int j=0;j<k;j++) ans[j]=inp[0][j]; 24 25 Item now; 26 for(int i=1;i<k;i++){ 27 while(!q.empty()) q.pop(); 28 now.b=0; 29 for(int j=0;j<k;j++){ 30 now.sum=ans[j]+inp[i][0]; 31 q.push(now); 32 } 33 int ind=0; 34 while(ind<k){ 35 now=q.top(); 36 q.pop(); 37 ans[ind++]=now.sum; 38 if(now.b+1<k){ 39 now.sum=now.sum-inp[i][now.b]+inp[i][now.b+1]; 40 now.b++; 41 q.push(now); 42 } 43 } 44 } 45 printf("%d",ans[0]); 46 for(int i=1;i<k;i++) printf(" %d",ans[i]); 47 printf(" "); 48 } 49 return 0; 50 }