標籤:namespace img 序列 技術分享 desc ane sample http 時間複雜度
1245 最小的N個和 http://codevs.cn/problem/1245/題目描述 Description
有兩個長度為 N 的序列 A 和 B,在 A 和 B 中各任取一個數可以得到 N^2 個和,求這N^2 個和中最小的 N個。
輸入描述 Input Description
第一行輸入一個正整數N;第二行N個整數Ai 且Ai≤10^9;第三行N個整數Bi,
且Bi≤10^9
輸出描述 Output Description
輸出僅一行,包含 n 個整數,從小到大輸出這 N個最小的和,相鄰數字之間用
空格隔開。
範例輸入 Sample Input
5
1 3 2 4 5
6 3 4 1 7
範例輸出 Sample Output
2 3 4 4 5
資料範圍及提示 Data Size & Hint
【資料規模】 對於 100%的資料,滿足 1≤N≤100000。
堆的基本操作+貪心
堆的基本操作講解,見隨筆:講解——堆http://www.cnblogs.com/TheRoadToTheGold/p/6238795.html
設輸入資料存在數組a[]和b[]中,heap[]為大根堆
為什麼題目要求最小的n個,我們卻要維護大根堆呢?看完以下幾個步驟再說。
1、排序:題目要求輸出最小的n個數,所以先將兩個數組從小到大排序
2、heap[]初始化:因為固定輸出n個數,所以把排序之後的數組a[1]依次與b[]的每一個數相加,和加入heap[]中
3、從a[2]開始枚舉每一個數,如果a中的第i個+b中的第1個(即b中最小的一個)>=heap[1](即堆中最大的元素),那麼結束枚舉。因為a是遞增的,b也是遞增的,後面的相加的和會更大。
如果a中第i個+b中第j個>=heap[1],那麼枚舉a的下一個,因為b中元素遞增,與同一個a相加後更大;枚舉a的下一個,b從第一個開始,可能會產生更小的。
4、步驟3中,在每加入一個元素之前,都要刪除堆中第一個(即最大的),因為加入的元素一定小於堆中第一個元素。
5、取出堆中的每一個元素,因為是大根堆,而題目要最小的n個,所以倒敘儲存。
6、輸出答案
由此可見為什麼要維護大根堆了吧!利用大根堆的第一個元素可以快速判斷a,b中的數還有沒有枚舉的必要。
有人可能說了,那我維護一個小根堆,不是也能判斷嗎?
的確,也能判斷。
但小根堆中最大的元素尋找時間複雜度為o(n/2),大根堆為O(1)。當然是大根堆快啦。
此處容易有一個理解偏差:認為小根堆中最大的元素就是heap[n],實際不是這樣:
#include<cstdio>#include<algorithm>using namespace std;int n,a[100001],b[100001],heap[100010],s,ans[100001];//s表示heap[]中的元素個數int init()//讀入最佳化 { int x=0,f=1;char c=getchar(); while(c<‘0‘||c>‘9‘) {if(c==‘-‘) f=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();} return x*f;}void insert(int k)//往堆中加入元素k { s++; int p=s; heap[s]=k; while(p>1&&k>heap[p/2]) { heap[p]=heap[p/2]; p/=2; } heap[p]=k;}void heapify(int t)//維護堆 { int left=t*2,right=t*2+1; int maxn=t; if(left<=s) maxn=heap[maxn]>heap[left] ? maxn:left; if(right<=s) maxn=heap[maxn]>heap[right] ? maxn:right; if(maxn!=t) { swap(heap[maxn],heap[t]); heapify(maxn); }}int get()//取出堆中最大值 { int top=heap[1]; heap[1]=heap[s]; s--; heapify(1); return top;}int main(){ n=init(); for(int i=1;i<=n;i++) a[i]=init(); for(int i=1;i<=n;i++) b[i]=init(); sort(a+1,a+n+1);//排序,對應第1步 sort(b+1,b+n+1); for(int i=1;i<=n;i++) insert(a[1]+b[i]);//堆得初始化,對應第2步 for(int i=2;i<=n;i++)//對應第3步 { if(heap[1]<=a[i]+b[1]) break; for(int j=1;j<=n;j++) { if(heap[1]<=a[i]+b[j]) break; get();//對應第5步 insert(a[i]+b[j]); } } while(s) ans[s]=get();//對應第6步 for(int i=1;i<=n;i++) printf("%d ",ans[i]);//對應第7步 }
剛開始逾時一個點,原因是兩個判斷break的語句沒有加等號。只想著相等的元素也要輸出,卻忽略了判斷的是堆中最大的元素,相不相等無所謂。
逾時的資料:n=10000,然後20000個1,所有的和都是2
codevs 1245 最小的N個和