球形空间产生器
luogu传送门
有一个球形空间产生器能够在n维空间中产生一个坚硬的球体。
现在,你被困在了这个n维球体中,你只知道球面上n+1个点的坐标,你需要以最快的速度确定这个n维球体的球心坐标,以便于摧毁这个球形空间产生器。
注意: 数据保证有唯一解。
输入格式
第一行是一个整数n。
接下来的n+1行,每行有n个实数,表示球面上一点的n维坐标。
每一个实数精确到小数点后6位,且其绝对值都不超过20000。
输出格式
有且只有一行,依次给出球心的n维坐标(n个实数),两个实数之间用一个空格隔开。
每个实数精确到小数点后3位。
数据范围
1≤n≤10
输入样例:
2
0.0 0.0
-1.0 1.0
1.0 0.0
输出样例:
0.500 1.500
正解应该是高斯消元
然后是在爬山法的例题里面看到的,感觉可以用模拟退火这种做法(
haha
冲了一下发现不太对头,就硬是过不去,刚开始在想是不是球心是到所有点最近的点,(马上否决
然后变成:到所有点方差最近的点
再然后debug输出方差发现就硬是到不了0,((
去观摩了dl的代码,感觉思路差不多,但是人家的起点和温度初始点选的不太一样
选点是按平均选而不是在区间内随机
改成一样的选点方式还是过不去,嘻嘻
T 0 = 1 e 2 , T E = 1 e − 6 , T_0 = 1e2, T_E = 1e-6, T0=1e2,TE=1e−6,
衰减是0.9999
到这里是60分左右,acm不友好(
然后发现:
这里算方差的时候拿距离的平方算,delta会变大,如果不符合条件跳过去的概率会小一点
基本稳A
但还是很玄学((
#include <bits/stdc++.h>
using namespace std;
const int maxn = 20;
int n;
double a[maxn][maxn];
double p[maxn], tmp[maxn], an[maxn], s[maxn];
double ans = 1e8;
double rand(double l, double r) {
return (double)rand() / RAND_MAX * (r - l) + l;
}
double calc() {
double res = 0;
double avg = 0;
for(int i = 0; i < n + 1; ++ i) {
double tmp = 0;
for(int j = 0; j < n; ++ j) {
tmp += (a[i][j] - p[j]) * (a[i][j] - p[j]);
}
s[i] = tmp;
avg += tmp;
}
avg /= n + 1;
for(int i = 0; i < n + 1; ++ i) {
res += (s[i] - avg) * (s[i] - avg);
}
res /= n + 1;
res = sqrt(res);
if(res < ans) {
ans = res;
for(int i = 0; i < n; ++ i) {
an[i] = p[i];
}
}
return res;
}
void simulate_anneal() {
for(double T = 1e2; T >= 1e-6; T = T * 0.99995) {
double x = calc();
for(int i = 0; i < n; ++ i) {
tmp[i] = p[i];
p[i] = rand(p[i] - T, p[i] + T);
}
double y = calc();
double delta = y - x;
if(exp(-delta / T) < rand(0,1)) {
for(int i = 0; i < n; ++ i) {
p[i] = tmp[i];
}
}
}
}
int main() {
cin >> n;
for(int i = 0; i < n + 1; ++ i) {
for(int j = 0; j < n; ++ j) {
cin >> a[i][j];
p[j] += a[i][j] / (n + 1);
}
}
while((double)clock() / CLOCKS_PER_SEC < 0.7) {
simulate_anneal();
}
//cout << ans << endl;
for(int i = 0; i < n; ++ i) {
cout << fixed << setprecision(3) << an[i] << " ";
}
return 0;
}
不过一直过不去样例(
可能到时候要分治一下,小数据暴力,大数据用模拟退火
晚点再补个爬山
太玄学了
爬山:调参又是很迷惑
#include <bits/stdc++.h>
using namespace std;
const int N = 15;
double a[N][N], ans[N], delta[N], dist[N];
int n;
void calc() {
double avg = 0;
for(int i = 0; i < n + 1; ++i) {
dist[i] = delta[i] = 0;
for(int j = 0; j < n; ++ j) {
dist[i] += (a[i][j] - ans[j]) * (a[i][j] - ans[j]);
}
dist[i] = sqrt(dist[i]);
avg += dist[i] / (n + 1);
}
for(int i = 0; i < n + 1; ++ i) {
for(int j = 0; j < n; ++ j) {
delta[j] += (dist[i] - avg) * (a[i][j] - ans[j]) / avg;
}
}
}
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n;
for(int i = 0; i < n + 1; ++i) {
for(int j = 0; j < n; ++j) {
cin >> a[i][j];
ans[j] += a[i][j] / (n + 1);
}
}
for(double T = 1e4; T >= 1e-6; T *= 0.99995) {
calc();
for(int j = 0; j < n; ++j) {
ans[j] += delta[j] * T;
}
}
for(int i = 0; i < n; ++ i) {
cout << fixed << setprecision(3) << ans[i] << " ";
}
return 0;
}