【AtCoder Beginner Contest 145】C – Average Length (300点)
問題
平面上でN個の座標が与えられる。全ての点を1回ずつ通るような動き方は\(N!\)通りあるが、これらの総移動距離の平均を求めよ。
元の問題: C – Average Length
解き方
二通りの方法が考えられます。
1. 順列を使って解く方法
順列(N!)の全探索を行って、それぞれの場合の移動距離を計算し、最後に平均を求めれば終了です。C++などでは、next_permutation などを使うと順列を生成できます。
2.各ノード間の移動が何回あるかを考える方法
特定の2つの頂点間の移動は、\(N!\)通り中に、\(2(N-1)!\)回だけ生じます。
これは、2頂点を1つの頂点とみなすと、\((N-1)!\)通りの移動の仕方があるので、2頂点間の順序も含めると2倍の\(2(N-1)!\)通りになるからです。
よって、それぞれの頂点間の距離の総和 sum に対して、
$$ sum \cdot \frac{2(N-1)}{N!} = sum \cdot \frac{2}{N}$$
で答えが求まります。
注意
今回はNが8以下なので、1つ目の順列を用いたプログラムでも計算できました。
ですがNが大きな値になった時は、2つ目の方法で解かないと実行時間がオーバーしてしまうでしょう。
プログラム例
順列を使って解くC++でのプログラムです。
#include <bits/stdc++.h>
using namespace std;
double distance(double xi, double yi, double xj, double yj) {
// 2点間の距離を求める関数
return sqrt((xi - xj) * (xi - xj) + (yi - yj) * (yi - yj));
}
int main() {
// 入力
int N;
cin >> N;
vector<double> x, y;
for (int i = 0; i < N; i++) {
double tmp_x, tmp_y;
cin >> tmp_x >> tmp_y;
x.emplace_back(tmp_x);
y.emplace_back(tmp_y);
}
// 距離を計算して distance_table に格納しておく
double distance_table[10][10]; // distance_table[i][j]: i,jの距離
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
distance_table[i][j] = distance(x[i], y[i], x[j], y[j]);
distance_table[j][i] = distance(x[i], y[i], x[j], y[j]);
}
}
// 順列の始めのケースを作成: {0,1,2,...,n-1}
vector<int> one_permutation_case;
for (int i = 0; i < N; i++) {
one_permutation_case.emplace_back(i);
}
double sum_distance = 0; // 総移動距離の合計
int case_number = 0; // 何通りあったか
do {
// 移動距離を sum_distance に加えていく
case_number++;
int now = one_permutation_case[0];
for (int i = 1; i < N; i++) {
int next = one_permutation_case[i];
sum_distance += distance_table[now][next];
now = next;
}
} while (next_permutation(one_permutation_case.begin(), one_permutation_case.end()));
// setprecision(10) が無いと精度が足りない
cout << setprecision(10) << sum_distance / case_number << endl;
return 0;
}
ディスカッション
コメント一覧
まだ、コメントがありません