UVA11997 K Smallest Sums
本篇是刘汝佳《算法竞赛入门经典——训练指南》的读书笔记。
知识点: 优先队列
解题思路:
先考虑\(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("\n"); 48 } 49 return 0; 50 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”