《k路并归问题》
模型一:给定n个数组,让你求n个数组中最小的k个数。
这里显然可以用到一种贪心的维护思路。利用小顶堆。
先将n个数组排成有序数组。
将每个数组的最小的那个数放入小顶堆中。
然后我们开始维护,很显然对于某一个数组中的某个数,如果它要被放入在小顶堆中,那他在该数组中前面位置的那个数,显然已经在队列中。
因为它前面的那个数比他小,按照最小的思路,肯定是先放入最小的。
模型二:UVA - 11997
给定k个数组,每个数组中有k个数,求在每个数组中选一个数,组成的和的最小的k个。
考虑递推的思路,假定我们当前维护到了第i个数组,且我们知道了前i - 1个数组每个取一个组成的最小k种方案。
那么我们就可以转移过来。转移的思路依旧考虑有序数组,我们先用第i个数组的最小的数和前k个最小的组成新的k个数。
放在小顶堆中,然后我们依旧用上面的维护思路,假定我们要变动的数在数组中的位置为p,那么下一个放入的肯定是p + 1和当前的组合。
所以要记录下下标。
// Author: levil #include<iostream> #include<stdio.h> #include<queue> #include<algorithm> #include<math.h> #include<stack> #include<map> #include<limits.h> #include<vector> #include<string.h> #include<string> using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int> pii; const int N = 1e5 + 5; const int M = 1e7 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e12 #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline LL read(){ LL 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<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } } using namespace FASTIO; vector<int> vec[1005]; LL ans[1005]; int main() { int k; while(~scanf("%d",&k)) { for(int i = 1;i <= k;++i) { vec[i].clear(); for(int j = 1;j <= k;++j) { int x;scanf("%d",&x); vec[i].push_back(x); } sort(vec[i].begin(),vec[i].end()); } for(int i = 1;i <= k;++i) ans[i] = vec[1][i - 1]; for(int i = 2;i <= k;++i) { priority_queue<pii,vector<pii>,greater<pii> >Q; for(int j = 1;j <= k;++j) { Q.push(pii{ans[j] + vec[i][0],0}); } for(int j = 1;j <= k;++j) { LL val = Q.top().first; int pos = Q.top().second; Q.pop(); if(pos + 1 < k) Q.push(pii{val - vec[i][pos] + vec[i][pos + 1],pos + 1});//删去p,与p + 1组合 ans[j] = val; } } for(int i = 1;i <= k;++i) printf("%lld%c",ans[i],i == k ? '\n' : ' '); } system("pause"); return 0; }