diverta 2019 Programming Contest 2 B問題
2019年6月15日開催のdiverta 2019 Programming Contest 2 に参加した.B問題の私の理解と解説.
B - Picking Up
問題はこちら.j番目のボールの座標からi番目のボールの座標へのベクトル(i<j)を全て調べて,出現回数が一番多いベクトルを(p, q)とすれば最小コストでの移動が可能になる.ただし,移動の順序は自由なので,逆向きでもよいことに注意する.
C++の実装例(#include は省略)
#define rep(i,n) for(long long i=0;i<n;i++)
using namespace std;
int main() {
int N;
cin >> N;
vector<int> x(N), y(N);
if(N==1) { // Nが1のときは,コスト1で確定.
cout << 1 << endl;
return 0;
}
rep(i, N) {
cin >> x[i] >> y[i];
}
// ベクトルの集合を得る.
int tmpx = 0;
int tmpy = 0;
priority_queue<pair<int, int>> vec;
for(int i=0; i<N; i++) {
for(int j=i+1; j<N; j++) {
tmpx = x[i] - x[j];
if(tmpx>0) { // xが正
vec.push(make_pair(tmpx, y[i]-y[j]));
} else if(tmpx==0){ // tmpx がゼロ なら y が正になるようにする.
tmpy = y[i]-y[j];
if(tmpy>0) vec.push(make_pair(tmpx, tmpy));
else vec.push(make_pair(-tmpx, -tmpy));
} else { // tmpx が負: xが正になるようにする.
vec.push(make_pair(-tmpx, -y[i]+y[j]));
}
}
}
// 出現したベクトルから最頻数を求める
int cnt = 0;
int maxcnt = 0;
pair<int, int> tmp_p = make_pair(0, 0);
int vecsize = N*(N-1)/2;
rep(i, vecsize) {
pair<int, int> p = vec.top();
vec.pop();
if(tmp_p.first==p.first && tmp_p.second==p.second){
cnt++; // 同じものが連続で現れたらカウントプラス
}
else { //違うものが現れたら,maxか判定し,tmp_pを更新, cntをリセット
if(cnt>maxcnt) maxcnt = cnt;
tmp_p.first = p.first;
tmp_p.second = p.second;
cnt = 0;
}
}
if(cnt>maxcnt) maxcnt = cnt; // 最後のベクトルがmaxか確認.
cout << N-maxcnt-1 << endl;
return 0;
}
上の実装の"//ベクトルの集合を得る"の部分では,j番目のボールの座標からi番目のボールの座標へのベクトル(i<j)を,x成分が0以外ならプラスになるように,x成分が0ならy成分がプラスになるように,方向をさだめて,priority_queue に格納することで,逆方向も含めた同じベクトルが連続して出てくるようにしている.priority_queue は格納した要素を昇順に(複数成分のときは第一成分から優先して)並べ替えて格納してくれるqueueである.例えば,そのまま(x[i]-x[j], y[i]-y[j])のとき,
(1,0) (0,1) (1,2) (-1,0) (0,-1) (0,-2)
となるものを,
(1,2) (1,0) (1,0) (0,2) (0,1) (0,1)
としている.これにより,ベクトルの出現個数を調べる際の計算を速くしている(本問題ではここまでしなくても計算時間は十分間に合う.この実装だと実行時間は1ms).
後半の" // 出現したベクトルから最頻数を求める"部分では,先ほど紹介した,
(1,2) (1,0) (1,0) (0,2) (0,1) (0,1)
のようなpriority_queueを順番に見ていき,連続して同じベクトルが出てきた回数の最大値を maxcnt に格納している.
この記事が気に入ったらサポートをしてみませんか?