http://poj.org/problem?id=2442
题意:略
算法:想了好久还是只想到一半啊,先考虑只有两行的情况,有一种方法是暴力O(n^2) + O(n^2 * log(n^2)),就是把所有的可能都列举出来
这样的方法显然过于浪费,因为有很多值根本不用去求。
n log(n)的方法:可以先将暴力的方案写下来,假设两行为A 和 B ,则所有的方案为
A1+B1 A1+B2 A1+B3 A1+B4.... A1+Bn
A2+B1 A2+B2 A2+B3 A2+B4.... A2+Bn
............
An+B1 An+B2 An+B3 An+B4... .An+Bn
上面第 i 行为Ai开始的所有方案,且每一行的第一个数都是最小的数(假设A B事先排好序)
从这个矩阵中取出前n小的数:,每行有一个指针,初始时都在1位置,先将第一列的数放进堆,取出最小的数,再将取出的数的那一行的指针后移一位,将移动到的位置山的数放入堆里,这样子连续取n次就可以了,这样子做的理由很简单,堆里面始终保存着n个元素,且这n个元素是分别包含A1 A2..An的最小的和,从这里面取一个数自然是当前最小的
两行搞定了,m行就是一样了,相当于每次处理两行即可,合并成一行当前答案,每读入一行再合并复杂度 m * n * log(n)。
#include <cstdio> #include <cstring> #include <set> #include <string> #include <iostream> #include <cmath> #include <vector> #include <map> #include <stack> #include <time.h> #include <queue> #include <cstdlib> #include <algorithm> using namespace std; #define lowbit(x) ((x)&(-(x))) #define sqr(x) ((x)*(x)) #define PB push_back #define MP make_pair #define foreach(it, x) for(typeof(x.begin()) it = x.begin(); it!=x.end();it++) typedef unsigned long long ULL; typedef long long lld; typedef vector<int> VI; typedef vector<string> VS; typedef pair<int,int> PII; const int maxn = 100010; int ans[4012]; int tmp[4012]; int num[4012]; int pos[2012]; priority_queue<pair<int,int> > st; int main() { int t,m,n; scanf("%d",&t); while(t--) { scanf("%d%d",&m,&n); for(int i = 1 ; i <= n ; i++) scanf("%d",&ans[i]); sort(ans+1,ans+n+1); for(int i = 2 ; i <= m ; i++) { for (int j = 1 ; j <= n ; j++) scanf("%d",&num[j]),pos[j] = 1; sort(num+1 , num+n+1); while(!st.empty()) st.pop(); for(int j=1;j<=n;j++) st.push( MP(-(ans[j] + num[1]) , j) ) ; int pt = 0; while(pt < n) { pair<int,int> pr = st.top(); st.pop(); tmp[++pt] = - pr.first; pos[pr.second] ++ ; st.push( MP(-(ans[pr.second]+num[pos[pr.second]]),pr.second) ) ; } for(int j = 1; j <= n ; j ++ ) ans[j] = tmp[j]; } for(int i=1;i<=n;i++) printf("%d%c",ans[i],i==n ? '\n' : ' '); } return 0; }