SRM 373 500pt: RoadCrossing

問題概要

横断歩道がある。横断歩道の幅はRで、車の幅はCである。人がN(<50)人いる。各人は時刻T[i]に速度V[i]で横断歩道を渡りはじめる。時刻A以降で車が横切ることのできる最小時刻を求める問題。車が横切るのに必要な時間は考えなくてよい。出てくる数字は1000以下。

考えたこと

  • 幾何とかシミュレーションでよくある特徴点全列挙かな?
  • 時刻を固定したら通過できるかどうかはO(N*log N)で判定できる。
  • 後は特徴点がいくつくらいあるか見積もってみよう。
  • まず、Aとか2000(考えられる最大値)を突っ込む( O(1) )。
  • 次に、確認に対して渡りはじめと渡り終わりと座標Cにいる時刻と座標R-Cにいる時刻を突っ込む( O(N) )。
  • 最後に、任意の二人の距離がCになるとき( O(N^2) )。
  • なので、計算量は全体としてO(N^3 * log N)で余裕。
  • doubleの計算になるけど、出てくる値が小さいのでEPSを適当にとれば通るはず。
  • 書いた。サンプル通った。投げた。落ちた。あれ?
  • 場合分けの座標Cと座標R-Cを書いてなかった。だから場合分けではコメントを書けとあれほど…。

practiceで通ったコード

計算量O(N^3 * log N)。

#include <vector>
#include <string>
#include <algorithm>
#include <sstream>
using namespace std;

const double EPS = 1e-9;

struct RoadCrossing {

	double passTime(vector <string> pedestrians, int roadWidth, int carWidth, int carArrival) {

		//候補点を全列挙する
		vector<double> eventline;
		eventline.push_back(carArrival); //最小値
		eventline.push_back(2000); //最大値


		//データ変換
		const int N = pedestrians.size();
		vector< pair<int,int> > walks; //(T, V)
		for(int i=0; i<N; i++){
			int x, y;
			stringstream ss(pedestrians[i]);
			ss >> x >> y;
			walks.push_back( make_pair(x, y) );

			eventline.push_back(x);
			eventline.push_back(x + (double)roadWidth/y);
		}

		//候補点列挙

		//2歩行者の距離がcarWidthになる場合
		for(int i=0; i<N; i++){
			for(int j=0; j<N; j++)if(i!=j){
				const double v1 = walks[i].second, v2 = walks[j].second;
				const double T1 = walks[i].first, T2 = walks[j].first;
				if(v1 != v2){
					const double t = ((v1*T1 - v2*T2) + carWidth) / (v1 - v2);
					eventline.push_back(t);
				}
			}
		}

		//歩行者の進行距離、残り距離がcarWidthになる場合
		for(int i=0; i<N; i++){
			const double v = walks[i].second, T = walks[i].first;
			const double t1 = (carWidth + v*T) / v;
			const double t2 = (roadWidth - carWidth + v*T) / v;
			eventline.push_back(t1);
			eventline.push_back(t2);
		}

		//全候補点をチェック
		sort(eventline.begin(), eventline.end());
		for(int i=0; i<(int)eventline.size(); i++){
			const double t = eventline[i];
			if(carArrival<=t && t<=2000.0 && check(t, walks, roadWidth, carWidth)){
				return t;
			}
		}
		return 2000.0;
	}

	//時刻timeに通れるかどうか判定する。O(N*log N)
	bool check(const double time, const vector<pair<int,int> >& walks,
					const int road_width, const int car_width){
		const int N = walks.size();

		//時刻timeにおける特徴点
		vector<double> pos;
		pos.push_back(0);
		pos.push_back(road_width);

		//時刻timeにおける各員の位置
		for(int i=0; i<N; i++){
			double v = walks[i].second, T = walks[i].first;
			double p = v*(time - T);
			if(0<=p && p<=road_width){
				pos.push_back(p);
			}
		}

		//判定
		sort(pos.begin(), pos.end());
		for(int i=0; i<(int)pos.size()-1; i++){
			double dist = pos[i+1] - pos[i];
			if(dist + EPS > car_width){
				return true;
			}
		}

		return false;
	}
};

バグを減らすための工夫

  • 意味の無い場合分けを減らすようにちゃんとかけたと思う。
  • 今回は意識して中間変数を多用した。まあ、悪くは無かった。