SRM 461 300pt: ColoringRectangle

問題概要

H*W(H,W<10^4)の長方形の板がある。赤い様々な直径の円盤をN(<50)枚、青いのをM枚持っている(直径<10^4)。最小何枚の円盤で板を被覆できるか求める問題。ただし、左から順に板を置いていき、同じ色の板が重なってはならない。

考えたこと

  • (はじめて参加したDiv 1のSRM。Div 1ではじめて通したのはこの問題)
  • 問題読むの2回目で問題の内容大体覚えてた。
  • 円盤は大きいものからつかうのが最善な気がする。
  • 赤からはじめるか青からはじめるかの2通りあるけど、両方シミュレーションしてやる。
  • 同じような処理が出てきたので関数にして処理する。
  • 計算でdoubleが出てくるけど流石にこれは避けようがない。
  • 何か通らないと思っていたら1ヶ所等号間違えてたのと、半径と直径を勘違いしていた。
  • 時間はかかったけど無事通った。当時と比べるとコードの内容もスコアも改善されていることが分かる。

practiceで通ったコード

計算量O(N+M)。書いてるときはオーバーフローのこと全然考えて無かった。通ったのは運が良かっただけ。

#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;

const int INF = 1<<29;
const double EPS = 1e-9;

struct ColoringRectangle {

	int chooseDisks(int width, int height, vector <int> red, vector <int> blue) {
		int ret = INF;
		sort(red.rbegin(), red.rend());
		sort(blue.rbegin(), blue.rend());

		ret = min(ret, solve(width, height, red, blue));
		ret = min(ret, solve(width, height, blue, red));

		return ret == INF ? -1 : ret;
	}

	int solve(int width, int height, const vector<int>& red, const vector<int>& blue){

		double left = 0;
		for(int i=0; ;i++){
			const vector<int> balls = (i&1)?red:blue;
			int id = i>>1;
			if(id >= (int)balls.size() || balls[id] < height){
				return INF;
			}
			int r = balls[id];

			double dist = 0.5 * sqrt(r*r - height*height);
			left += 2*dist;
			if(left > width - EPS){
				return i + 1;
			}

		}

		return INF;
	}

};