POJ-2442: Sequence
keyword
分割統治法 C++
問題概要
長さN(<2000)の数列がM(<200)個ある。それぞれの数列から一つずつ選んで和を作る。作ることのできる和(取ってきた場所が違うなら値が同じでもよい)のうち、小さい方から順にN個を出力する問題。
解法
分割統治法で処理する。各区間に対して、和を小さい方からN個返すようにする。mergeの部分は、N*(1/1 + 1/2 + 1/3 + 1/4 + ... + 1/N)で処理できる。
最初はpriority_queueを使ってdijkstraっぽく書いていたけど、やっぱりコンテナにvectorを突っ込むのはコストが大きい(回転などの操作の処理が重くなる)のでTLEした。
分割統治法。計算量O(M*log(M)*N*log(N))。
#include <cstdio> #include <vector> #include <algorithm> using namespace std; const int MAX_M = 109; const int MAX_N = 2009; vector<int> ass[MAX_M]; vector<int> ans; int N, M; //[a, b) vector<int> rec(int a, int b){ if(b-a==1){ return ass[a]; } int mid = (a+b)/2; vector<int> as = rec(a,mid); vector<int> bs = rec(mid,b); vector<int> cands; cands.reserve(16000); for(int i=0; i<N; i++){ for(int j=0;(i+1)*(j+1) <= N; j++){ cands.push_back(as[i] + bs[j]); } } sort(cands.begin(), cands.end()); cands.erase(cands.begin() + N, cands.end()); return cands; } void print(){ for(int i=0; i<N; i++){ printf("%d%c",ans[i], i==N-1?'\n':' '); } } int main(){ int T; for(scanf("%d",&T); T--; ){ scanf("%d%d",&M,&N); for(int i=0; i<M; i++){ ass[i].clear(); ass[i].reserve(N); for(int j=0; j<N; j++){ int x; scanf("%d",&x); ass[i].push_back(x); } sort(ass[i].begin(), ass[i].end()); } ans = rec(0,M); print(); } return 0; }
Dijkstraっぽい解法。計算量O(M^2*N*log(N*M))。O(M*N*log(N*M))でないことに注意。TLEする。
#include <cstdio> #include <vector> #include <queue> #include <set> #include <algorithm> using namespace std; typedef pair<int, vector<int> > state; typedef unsigned long long ul; const int MAX_M = 109; const int MAX_N = 2009; vector<int> ass[MAX_M]; vector<int> ans; int N, M; inline ul hash(vector<int>& xs){ ul ret = 0, base = 1; for(int i=0; i<M; i++){ ret += (ul)xs[i] * base; base *= 1000000007; } return ret; } void solve(){ priority_queue<state, vector<state>, greater<state> > Q; set<pair<int, ul> > visited; { int sum = 0; vector<int> ids; for(int i=0; i<M; i++){ ids.push_back(0); sum += ass[i][0]; } Q.push( make_pair(sum, ids) ); visited.insert( make_pair(sum, hash(ids)) ); } while(!Q.empty() && (int)ans.size() < N){ state tp = Q.top(); Q.pop(); int sum = tp.first; ans.push_back(sum); vector<int> ids = tp.second; for(int j=0; j<M; j++){ if(ids[j] < N-1){ int tmp = ids[j]; while(ids[j] < N-1 && ass[j][tmp] == ass[j][++ids[j]]){ ans.push_back(sum); } if(ids[j] < N){ state ns(sum + ass[j][ids[j]] - ass[j][tmp], ids); if(visited.insert( make_pair(ns.first, hash(ids)) ).second){ Q.push(ns); } } ids[j] = tmp; } } } } void print(){ for(int i=0; i<N; i++){ printf("%d%c",ans[i], i==N-1?'\n':' '); } } int main(){ int T; for(scanf("%d",&T); T--; ){ scanf("%d%d",&M,&N); for(int i=0; i<M; i++){ ass[i].clear(); ass[i].reserve(N); for(int j=0; j<N; j++){ int x; scanf("%d",&x); ass[i].push_back(x); } sort(ass[i].begin(), ass[i].end()); } ans.clear(); solve(); print(); } return 0; }