SRM 367 500pt: DeviceProgramming
問題概要
一列にデータが並んでいて、互いに素な区間がN(<50)個ある。区間の長さや初期位置は10^9以下である。この区間全てをいくつかの小区間でカバーする。ひとつの小区間にかかるコストは区間の長さ+overhead(<1000)で、ひとつの小区間のコストはmaxPacketSizeを越えてはいけない。必要な最小コストを求める問題。
考えたこと
practiceで通ったコード
計算量O(N^2 * log N * maxPacketSize)。
#include <vector> #include <algorithm> #include <cstdio> using namespace std; typedef long long int64; const int64 INF = 1LL<<61; struct DeviceProgramming { int64 minBytes(vector <int> offset, vector <int> size, const int max_packet_size, const int overhead) { const int N = offset.size(); const int max_data_size = max_packet_size - overhead; int64 ret = 0; //データをまとめる 開区間[from, to) vector< pair<int64,int64> > datas; for(int i=0; i<N; i++){ datas.push_back( make_pair(offset[i], offset[i]+size[i]) ); } sort(datas.begin(), datas.end()); //全てのデータを長さ大体3000以下にする。 for(int i=0; i<N; i++){ int len = datas[i].second - datas[i].first; if(len > 2000){ int64 packet_count = (len - 2000)/max_data_size; int64 total_packet_size = max_packet_size * packet_count; int64 total_data_size = max_data_size * packet_count; ret += total_packet_size; datas[i].second -= total_data_size; for(int j=i+1; j<N; j++){ datas[j].first -= total_data_size; datas[j].second -= total_data_size; } } } //座標圧縮 vector<int64> compress; vector<int> attributes; for(int i=0; i<N; i++){ for(int64 k=datas[i].first; k<datas[i].second; k++){ compress.push_back(k); attributes.push_back(i); } } //ソートとかはしなくてよい //DPテーブルの初期化 const int M = compress.size(); vector<int64> dp(M+1,INF); dp[0] = 0; //DP for(int i=0; i<M; i++){ //現在位置の情報 int color = attributes[i]; int cur = compress[i]; int next_longest_index = get_index(compress, cur + max_data_size - 1); //全部使う if(next_longest_index >= 0){ dp[next_longest_index + 1] = min(dp[next_longest_index + 1], dp[i] + max_packet_size); } //キリの良いところまで使う for(int j=color; j<N; j++)if( datas[j].second - cur <= max_data_size){ int next_index = get_index(compress, datas[j].second - 1) + 1; dp[next_index] = min(dp[next_index], dp[i] + (datas[j].second - cur) + overhead); } } ret += dp.back(); return ret; } int get_index(const vector<int64>& compress, int64 val){ if(!binary_search(compress.begin(), compress.end(), val)) return -1; return distance(compress.begin(), lower_bound(compress.begin(), compress.end(), val)); } };
計算量を下げる
座標圧縮とか面倒くさいことしなくても、ある区間の途中は必ず埋まるんだからdp[i] = (i-1番目までをカバーする最小値)とすればdp[i]からdp[i+1],..,dp[N]までの遷移を考えるとO(N^2)で解ける。