bzoj1094: [ZJOI2007]粒子运动

【算法】

 计算几何

其中一共有三个主要计算:求点的碰撞所需时间,碰撞后的速度,两点的最短距离。用向量都可以得到。

【注意】

 WA了两次,第一次是法向量求错,第二次是因为一个奇葩的思路,并没有求所有点任意时刻的最短距离(我简直脑。。残。。)。这题竟然写了5个多小时委屈

【参考资料】

 1、某神的博客1

 2、某神的博客2

值得一提的是,(1)中的计算过程很详细,写的也很好(我在这里就不重复了),但当我看到复平面这三个字后我就果断不看了哭

还有就是在最后计算距离时,(2)中的代码

double solve(int i,int j)
{
    //......
    while(p<k||q<k)//应该同时满足
    //......
}
这一句貌似有点问题,按照题意一个点在第k次碰撞就已经消亡了,对于特殊的数据 会产生错误的结果。

例如
0 0 10000
2 50
1948.000 1642.000 406.000 925.000
4203.000 5406.000 351.000 212.000

(2)程序输出为7.670,而正确结果应该是163.253

【代码】

/**************************************************************
    Problem: 1094
    User: zhengly123
    Language: C++
    Result: Accepted
    Time:176 ms
    Memory:2848 kb
****************************************************************/
 
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <math.h>
#include <algorithm>
#include <string.h>
#define MEM(a) memset(a,0,sizeof(a))
#define CPY(a,b) memcpy(b,a,sizeof(b))
using namespace std;
const int INF = 999999999;
const double INFdb = 1e200;
const int MAXN = 200;
const double eps = 1e-8;
 
inline double MIN(double a, double b){ return (a < b) ? a : b; }
 
inline double MAX(double a, double b){ return (a > b) ? a : b; }
 
int zero(double a)
{
    if (fabs(a) < eps) return 0;
    else return (a>0) ? 1 : -1;
}
 
struct point
{
    double x, y;
    point(){}
    point(double a, double b){ x = a; y = b; }
    friend point operator+(const point&a, const point&b){ return point(a.x + b.x, a.y + b.y); }
    friend point operator -(const point&a, const point&b){ return point(a.x - b.x, a.y - b.y); }
    friend point operator *(const double&k, const point&a){ return point(k*a.x, k*a.y); }//数乘
    friend point operator /(const point&a, const double&b){ return point(a.x / b, a.y / b); }
    friend double operator *(const point&a, const point&b){ return a.x*b.x + a.y*b.y; }//点积
    friend double operator /(const point&a, const point&b){ return a.x*b.y - a.y*b.x; }//叉积,用^优先级有问题
    friend bool operator <(const point&a, const point&b){ return (a.x - b.x < -eps) || (fabs(a.x - b.x) < eps && (a.y - b.y) < -eps); }
    friend bool operator ==(const point&a, const point&b){ return zero(a.x - b.x) && zero(a.y - b.y); }
    double dis(){ return (sqrt(x*x + y*y)); }
    double spr(){ return(x*x + y*y); }
    void input(){ scanf("%lf%lf", &x, &y); }
    void output(){ printf("%lf %lf\n", x, y); }
}p[MAXN][MAXN], v[MAXN][MAXN], cir;
 
int num[MAXN], n, k;
double st[MAXN][MAXN], cx, cy, r;
 
void calc(int c,int k)//计算碰撞时间
{
    point t = p[k][c] - cir;
    double detla = 4 * (t*v[k][c])*(t*v[k][c]) - 4 * (v[k][c] * v[k][c])*(t*t - r*r);
    if (zero(detla) == 0) detla = 0;
    st[k + 1][c] = st[k][c] + (-2 * t*v[k][c] + sqrt(detla)) / (2 * v[k][c] * v[k][c]);
}
 
double cal(int c1, int c2, int k1, int k2, double strt, double endt)//计算最小距离
{
    double ans, a, b, c, t;
    point p1 = p[k1][c1] - p[k2][c2] - st[k1][c1] * v[k1][c1] + st[k2][c2] * v[k2][c2], p2 = v[k1][c1] - v[k2][c2];
    a = p2*p2;
    b = 2 * p1*p2;
    c = p1*p1;
    if (zero(a)==0)
    {
        if (b > 0) ans = strt;
        else ans = endt;
        return sqrt(b*ans + c);
    }
    else
    {
        t = -b / (2 * a);
        if (t <= strt || zero(strt - t) == 0) ans = strt;
        else if (t >= endt || zero(endt - t) == 0) ans = endt;
        else ans = t;
        return sqrt(a*ans*ans + b*ans + c);
    }
}
 
void work()
{
    int i, j, n, k1, k2;
    double minn, ans = INFdb, t;
    point ps, pi, pp;
    cir.input();
    scanf("%lf%d%d", &r, &n, &k);
    for (i = 1; i <= n; ++i)
    {
        p[0][i].input();
        v[0][i].input();
    }
    for (i = 1; i <= n; ++i)
    {
        for (j = 0; j < k; ++j)
        {
            calc(i, j);
            t = st[j + 1][i] - st[j][i];//转折前时间
            p[j + 1][i] = p[j][i] + t*v[j][i];//转折点
            pp = p[j + 1][i] - cir;//对称轴
            pi = point(-pp.y, pp.x);//法向量
            v[j + 1][i] = (2 * (v[j][i] * pi / (pi*pi))) *pi - v[j][i];
            ++num[i];
        }
    }
    for (i = 1; i <= n; ++i) for (j = i + 1; j <= n; ++j)
    {
        k1 = k2 = 0;
        while (k1 < k&&k2 < k)
        {
            ans = MIN(ans, cal(i, j, k1, k2, MAX(st[k1][i], st[k2][j]), MIN(st[k1 + 1][i], st[k2 + 1][j])));
            if (st[k1 + 1][i] < st[k2 + 1][j]) ++k1;
            else ++k2;
        }
    }
    printf("%.3lf\n", ans);
}
 
int main()
{
    work();
    return 0;
}


已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页