1. 程式人生 > >UVA 11997 K Smallest Sums 優先隊列+歸並 STL

UVA 11997 K Smallest Sums 優先隊列+歸並 STL

出隊 元組 lap 數值 題目 優先 emp 最小堆 std

  題目鏈接: UVA......

  題目描述: 有K組數, 每組數有K個, 所以每組數各選一個加和有k^k種情況, 要求輸出其中的最小的前k種, 從小到大輸出

  解題思路: 首先對於兩個數組取前K個, 構造二元組(s, b) 其中s = Aa + Bb , a, b 為下標。 為什麽不用三元組(s,a,b)呢, 因為二元組完全可以表示三元組, 下一個元素就是s+B[i+1]-B[i] .

      

        我們需要把這k^2個和組織成如下k個有序表.(A, B是有序的哦)

        表1:A1+B1<=A1+B2<=......<=A1+Bk

        表2: A2+B1<=A2+B2<=......<=A2+Bk

        表k:Ak+B1<=AK+B2<=......<=Ak+Bk

        再說這種構造方法為什麽可行, 這種構造方法保證了當最小的出隊之後, 下一個推進的一定是最優的, 因為如上圖所示, 對於每組數, 相鄰兩項相差的都是B[i]-B[i-1], 而推出的機制又保證了數值的最小, 所以與推出的數據相差最小的一定是推出數據的後一個元素

        最後再兩兩歸並算出最終結果, 復雜度是n^2logn(排序)

  代碼:

技術分享
#include <iostream>
#include <cstdio>
#include 
<cstring> #include <string> #include <algorithm> #include <queue> using namespace std; //priority_queue<int> heap; //priority_queue<int, vector<int>, greater<int>() > mm; struct Item { int s, b; // s = Aa + Bb 所以用下標b和sum二元組表示 k個數組中的元素 bool operator < ( const
Item & r ) const { return s > r.s; } }; const int MAXN = 800; int a[MAXN][MAXN]; int k; void merge( int * A, int * B, int * C ) { // A,B是待處理數組,C是結果數組 int cnt = 0; priority_queue<Item> pq; for( int i = 0; i < k; i++ ) { pq.push((Item){A[i]+B[0], 0}); // 將新建的k個數組中的第一個推進隊列, 最小的肯定在其中 } while(!pq.empty()) { Item t = pq.top(); // 通過最小堆得到最小值 pq.pop(); C[cnt++] = t.s; if( t.b + 1 < k ) { pq.push((Item){t.s + (B[t.b+1]-B[t.b]), t.b + 1}); // 推進彈出隊列的數組中下一個元素(為什麽可行題解中會解釋) } if( cnt == k ) break; } } int main() { while( ~scanf( "%d", &k ) ) { memset(a, 0, sizeof(a)); for( int i = 0; i < k; i++ ) { for( int j = 0; j < k; j++ ) { scanf( "%d", &a[i][j] ); } sort(a[i], a[i] + k); } for( int i = 1; i <= k-1; i++ ) { merge(a[0], a[i], a[0]); } for( int i = 0; i < k; i++ ) { if( i == 0 ) { printf( "%d", a[0][i]); } else { printf( " %d", a[0][i]); } } printf( "\n" ); } return 0; }
View Code

  思考: 一開始我用了錯誤的思路, 還以為很對......就是找出每組最小的數, 再找出次小的數推入隊列, 這樣不能保證是最小的, 因為如果有兩個分別大一, 而有一個大三,這個算法會選擇大三的......所以這道題難點就是如何構造這個優先隊列, 一定要能夠保證推出的數的下一個數是最小的。看來以後做題不能直接想當然的上手去寫, 寫之前多多思考, 仔細想想自己的算法是否能夠滿足自己想達到的目的, 再去動筆寫

UVA 11997 K Smallest Sums 優先隊列+歸並 STL