Luogu3945 | 三体问题 (天体物理+计算几何)

最近终于把《三体Ⅰ·地球往事》和《三体Ⅱ·黑暗森林》看完了!
为了快点认识题目中的歌者文明,已经开始第三部了!

题目背景

@FirstLight0521 出题人在这里哦~

三体人所居住的星系由于三体运动的不确定性而导致三体星人生活动荡不安,善良的人类程序员(也就是你了!伟大的英雄!)决定帮助愚蠢得连程序都不会写的三体星人模拟天体的运动轨迹。这时,无聊的“歌者”文明决定戏弄一下你,于是给三体星系添加了一些新的星体。

题目描述

输入 \(N\) 个天体与他们在空间中的坐标 \((xi,yi,zi)\) 、初速度 \((vx,vy,vz)\) 与质量 \(Mi\) ,已知三体世界受到“歌者”影响时间的流动不是连续的(每 \(0.01\) 秒钟刷新一次),天体均视为质点,求 \(t\) 时刻所有天体的坐标。

本题 \(G\)\((6.67408×10^{-11})\) ,在代码中可以写成:

#define G 6.67408e-11

当你的答案与标准答案的相对误差不超过 \(0.5%\) 的时候,你在本测试点得到AC。

也就是说,保留多少位小数你可以自行确定。

标准答案将会保留 \(12\) 位小数。本题开启 \(SPJ\) 判断你的答案是否正确。

输入格式

第一行输入天体数 \(N\) 与时刻 \(t\) ,接下来逐行输入以空格分隔的各天体坐标、质量与初速度(三个方向上的分速度)。

输出格式

\(N\) 行,每行为第 \(i\) 个天体在t时刻的坐标 \(xi,yi,zi\) ,空格隔开。

输入输出样例

输入 #1

3 100
0 10 0 10000000 0.006207480877613 0 0
8.660254037844 -5 0 10000000 -0.003103740438807 -0.00537583613352 0
-8.660254037844 -5 0 10000000 -0.003103740438807 0.00537583613352 0

输出 #1

0.620349511786 9.980741705470 0.000000000000
8.333401109655 -5.527609289167 0.000000000000
-8.953750621441 -4.453132416303 0.000000000000

输入 #2

3 100
0 10 0 10000000 0.06207480877613 0 0
8.760254037844 -5 0 10000000 -0.03103740438807 -0.0537583613352 0
-8.660254037844 -5 0 10000000 -0.03103740438807 0.0537583613352 0

输出 #2

6.204092324054 9.982347016794 0.000000000000
5.642963405596 -10.364100727695 0.000000000000
-11.747055729651 0.381753710901 0.000000000000

说明/提示

\(3 <= N <= 30\)\(0 <= T <= 100\)。初始坐标范围为 \([-100, 100]\) 。质量在 \(int64\) 范围内。

——————————————————————————————————————————
首先彻底理解题意比较重要,题目描述中有一句很关键的话:时间的流动不是连续的(每 \(0.01\) 秒钟刷新一次)

这就等于告诉我们了我们所研究的只是一个粗糙的三体模型,不需要考虑各数值在任意时刻的变化情况。

而只考虑每极短时间( \(0.01\) 秒)的变化,根据物理知识,变化量就是各行星的坐标、速度和加速度。

我们在某 \(0.01\) 秒开始之前首先计算所有行星间因为万有引力产生的加速度,

然后根据高中物理学的微元法,视每个 \(0.01\) 内都为匀速直线运动,依次更新行星速度和坐标。

为了方便计算,我们将所有的矢量正交分解,即可把在同方向上的影响直接相加。

公式推导过程如下:

万有引力公式:\(F=G\frac{M·m}{r^2}\),加速度 \(a=G\frac{M}{r^2}\)

接着通过下图的几何关系就可以算出加速度在某方向上的分量(三维也是同理)。

代码如下:

#include <bits/stdc++.h> 
#define G 6.67408e-11
using namespace std;
int n;
double t,t0=0.01;
struct ball {
    double m,x,y,z,vx,vy,vz,ax,ay,az;
    void read() { scanf("%lf%lf%lf%lf%lf%lf%lf",&x,&y,&z,&m,&vx,&vy,&vz); }
    void pri() { printf("%.12lf %.12lf %.12lf\n",x,y,z); }
}body[233];

inline void along(int num) {
        body[num].ax=body[num].ay=body[num].az=0.0;
        for (int i=1;i<=n;i++) {
        double d2,px,py,pz;
        if (i==num) continue;
        px=body[i].x-body[num].x;
        py=body[i].y-body[num].y;
        pz=body[i].z-body[num].z;
        d2=px*px+py*py+pz*pz;
        //计算加速度的分量
        body[num].ax+=G*body[i].m/(d2*sqrt(d2))*px;
        body[num].ay+=G*body[i].m/(d2*sqrt(d2))*py;
        body[num].az+=G*body[i].m/(d2*sqrt(d2))*pz;
    }
}

int main() {
    scanf("%d%lf",&n,&t);
    for (int i=1;i<=n;i++) body[i].read();
    for (double now=0.00;now<=t;now+=t0) {
        //先全部更新计算完影响之后,再移动。
        for (int i=1;i<=n;i++) along(i);
        for (int i=1;i<=n;i++) {
            body[i].vx+=body[i].ax*t0;
            body[i].vy+=body[i].ay*t0;
            body[i].vz+=body[i].az*t0;
            body[i].x+=body[i].vx*t0;
            body[i].y+=body[i].vy*t0;
            body[i].z+=body[i].vz*t0;
        }
    }
    for (int i=1;i<=n;i++) body[i].pri();
    return 0;
}

posted @ 2020-02-28 17:12  暖暖草果  阅读(916)  评论(0编辑  收藏  举报