技術的なやつ

技術的なやつ

2.3 AOJ-0006 to 0010

今日もAOJの問題を解いていた。

0006

文字列の反転問題。

0007

毎週5%の利子がついて、しかも1000円未満は切り上げられるという闇金業者の借金が、n週間後にはいくらになるか、という教育上よろしいのか疑われる問題。
1000円未満の切り上げ処理がポイント。むしろそこしかポイントが無い。

//一の位から数えてketa桁未満で繰り上げる
void keta_up(int &num, int keta){
	int p = pow(10, keta);
	int n = num / p;
	if(num-n*p > 0){
		num /= p;
		num += 1;
		num *= p;
	}
}

0008

0~9なる整数a,b,c,dで、a+b+c+d=nなる組み合わせは何通りか、という問題。
探索系の問題だけど、この場合はコレで解ける。

#include <iostream>

using namespace std;

int main(){
	int n;
	while(cin >> n){
		int ans_num = 0;
		for(int a=0; a<10; ++a){
			for(int b=0; b<10; ++b){
				for(int c=0; c<10; ++c){
					for(int d=0; d<10; ++d){
						if(a+b+c+d == n)	++ans_num;
					}
				}
			}
		}
		cout << ans_num << endl;
	}
	
	return 0;
}

こんな問題もあったねぇ。

0009

n以下の素数はいくつあるか、という問題。範囲が決められているのでエラトステネスの篩を使う。これ以外の選択肢は無い。篩のために使う変数は、グローバルにしておくこと(スタック領域じゃオーバーフローを起こす)。

#include <iostream>
#include <vector>

using namespace std;

const int MAX_NUM = 1000000;

bool prime_flag[MAX_NUM];
vector<int> prime;

void check_prime(){
	prime_flag[0] = false;
	for(int i=1; i<MAX_NUM; ++i){
		if(prime_flag[i] == true){
			int num = i+1;
			for(int j=2*num; j<MAX_NUM+1; j+=num){
				prime_flag[j-1] = false;
			}
			prime.push_back(i+1);
		}
	}
}

int main(){
	for(int i=0; i<MAX_NUM; ++i)	prime_flag[i] = true;
	check_prime();
	int n;
	while(cin >> n){
		int i;
		for(i=0; i<(int)prime.size(); ++i){
			if(prime[i] > n)	break;
		}
		cout << i << endl;
	}
	
	return 0;
}

0010

三点が与えられるので、それで作られる三角形の外接円の中心座標と半径を求めよ、とのこと。
中心座標の導出がめっちゃめんどい。2弦の垂直二等分線の交点がそれなので、それを導出してみる。
まず、(x1,y1)と(x2,y2)の垂直二等分線は以下のような直線である。

2(x_2-x_1)x + 2(y_2-y_1)y = x_2^2 - x_1^2 + y_2^2 - y_1^2

この時点で割と吐き気がする。
よって、

2(x_2-x_1)x + 2(y_2-y_1)y = x_2^2 - x_1^2 + y_2^2 - y_1^2
2(x_3-x_2)x + 2(y_3-y_2)y = x_3^2 - x_2^2 + y_3^2 - y_2^2

この連立方程式を解けば良い。ゲェ~ッ。
行列使って解いて、解は以下のようになる。

x=\frac{1}{2}\cdot\frac{(y_3-y_2)(x_2^2-x_1^2+y_2^2-y_1^2) - (y_2-y_1)(x_3^2-x_2^2+y_3^2-y_2^2)}{(x_2-x_1)(y_3-y_2)-(y_2-y_1)(x_3-x_2)}
y=\frac{1}{2}\cdot\frac{-(x_3-x_2)(x_2^2-x_1^2+y_2^2-y_1^2) + (x_2-x_1)(x_3^2-x_2^2+y_3^2-y_2^2)}{(x_2-x_1)(y_3-y_2)-(y_2-y_1)(x_3-x_2)}

これはひどい。
一応、ちゃんと正解は出せる。動作も非常に高速である。見た目はともかく。
半径の導出は簡単なので省略。