1 条题解

  • 0
    @ 2025-8-24 21:39:12

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar XZYQvQ
    **

    搬运于2025-08-24 21:39:12,当前版本为作者最后更新于2018-03-27 22:22:04,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    //安利一下自己的博客啦:http://www.k-xzy.xyz/archives/4979

    Ps:几万年前退役的我看了看评论发现居然还有人看这篇文章,并且提出博客地址有误的问题。啊,确实如此,我退役当天博客就关闭啦,但是我的一位好心的同学 Remmina 要走了网站数据并且一直当自己的博客开着,我的文章也都能在里面找到,地址是 https://www.mina.moe。因此这篇文章也可以在https://www.mina.moe/archives/4979上找到。在此也特别感谢 Remmina 同学,也感谢各位耐心阅读一个早就凉透的OIer在最美好的时光写下的这篇“不务正业”的文章。

    1. 题目

    传送门= ̄ω ̄=

    2. 题解

    网上基本上所有Dalao都是写的“三分套三分”Orz

    (好像WJMZBMR写的是模拟退火,为啥我一开始写模拟退火挂了呢QvQ可能我太弱了)

    先讲下这题吧。

    首先lxhgww的路径一定是从AA点上传送带ABAB,走xx个单位时间(0xABP0\leq x\leq \frac{AB}{P}),再下ABAB传送带,徒步走上CDCD传送带(不一定是在CC点上传送带),走yy个单位时间到达DD点(0yCDQ0\leq y\leq \frac{CD}{Q})

    设用时为f(x,y)f(x,y)x,yx,y的意义见上文)

    则:

    f1(t)f1(t)表示从AA点出发,沿着ABABtt个单位时间后到达的坐标。

    f2(t)f2(t)表示从DD点出发,沿着DCDCtt个单位时间后到达的坐标。

    那么:

    f(x,y)=dis(f1(x),f2(y))R+x+yf(x,y)=\frac{dis(f1(x),f2(y))}{R} + x + y

    disdis表示求两点之间的欧几里得距离)

    好的,现在这个问题已经转化为了求一个二元函数的最小值

    可以把函数放到三维空间里看成一个物体,要在一个矩形区域内找物体的最低点。

    。。。

    写挂模拟退火以后我就写了个粒子群优化算法。。。

    这算法真NB啊,什么数学推导都不需要,完全把函数当做随机函数看都能过掉!

    这个算法是模仿生物(鸟类)捕食的过程。把一堆粒子随机撒在函数定义域内,然后每次找出所有粒子中所在位置对应的函数值最优的粒子,并且让大家都尽量向这里加速

    每个粒子都是有自己的惯性的,这样可以增加随机性,以免卡在局部最优解导致答案错误。

    总之就是模拟真实的自然界啦。。。

    具体很难说清楚,可以去网上搜一下这个算法,很玄妙的QvQ

    但是好像网上很少有(或者说可能根本没有)把粒子群优化算法这种人工智能算法应用于OI竞赛的,所以这里发一下代码,里面会写上注释的,还是看代码比较好懂=。=(其实是手缝针啦打字有点不舒服)

    另外实验结果表明,粒子群算法更注重粒子个数。。。像我一开始那样开20个粒子就冲上去爆〇的做法真的不可取QvQ

    代码:

    #include <bits/stdc++.h>
    
    #define BS (505)//粒子数组大小
    #define eps (1e-8)
    
    using namespace std;
    
    const int cnt = 500;//粒子个数
    
    struct vec//向量
    {
    	double x, y;
    	vec operator + (const vec oth) const
    	{
    		return (vec){x + oth.x, y + oth.y};
    	}
    	vec operator - (const vec oth) const
    	{
    		return (vec){x - oth.x, y - oth.y};
    	}
    	vec operator * (const double d) const
    	{
    		return (vec){x * d, y * d};
    	}
    	vec operator / (const double d) const
    	{
    		return (vec){x / d, y / d};
    	}
    };
    
    vec A, B, C, D, P1, P2;
    
    double P, Q, R, rx, ry;
    
    vec f1(double x) {return A + P1 * x;}//和上文定义的f1意义相同
    
    vec f2(double x) {return D + P2 * x;}//和上文定义的f2意义相同
    
    double Squ(double a) {return a * a;}
    
    double dis(vec a, vec b)//求a,b的欧几里得距离
    {
    	return sqrt(Squ(a.x - b.x) + Squ(a.y - b.y));
    }
    
    double f(double x, double y)//和上文定义的f意义相同
    {
    	return x + y + dis(f1(x), f2(y)) / R;
    }
    
    double Rand() {return (double)rand() / RAND_MAX;}//返回一个[0,1]的随机实数
    
    struct Bird {double vx, vy, x, y, f, pb, pbx, pby;} b[BS];//粒子们
    
    //vx,vy是速度向量,x,y是位置向量,f是当前位置的函数值,pb是该粒子历史最优值
    
    double w = 1, gb = 1e233, gbx, gby;
    
    //w是精度权重, gb是全局目前最优值, gbx,gby是取到全局最优值时的自变量x,y
    
    void Update(int a)//更新粒子a的状态,这里具体原理请见算法实现原理
    {
    	b[a].vx = b[a].vx * w + Rand() * w * (gbx + b[a].pbx - b[a].x * 2);
    	b[a].vy = b[a].vy * w + Rand() * w * (gby + b[a].pby - b[a].y * 2);
    	//b[a].v * w是因为有惯性,另外的项则是尽量像更优解靠近
    	b[a].x += b[a].vx, b[a].y += b[a].vy;//更新位置向量
    	//下面4行是防止粒子出界,出界则反弹回来(这里我没在网上找到相关资料介绍如何处理出界,所以是自己YY的)
    	if (b[a].x < 0) b[a].x = 0, b[a].vx = -b[a].vx;
    	if (b[a].y < 0) b[a].y = 0, b[a].vy = -b[a].vy;
    	if (b[a].x > rx) b[a].x = rx, b[a].vx = -b[a].vx;
    	if (b[a].y > ry) b[a].y = ry, b[a].vy = -b[a].vy;
    	b[a].f = f(b[a].x, b[a].y);//计算当前位置函数值
    	if (b[a].f < b[a].pb)//更新局部最优解
    		b[a].pbx = b[a].x, b[a].pby = b[a].y, b[a].pb = b[a].f;
    }
    
    int main(int argc, char const* argv[])
    {
    	scanf("%lf%lf%lf%lf", &A.x, &A.y, &B.x, &B.y), P1 = B - A;
    	scanf("%lf%lf%lf%lf", &C.x, &C.y, &D.x, &D.y), P2 = C - D;
    	scanf("%lf%lf%lf", &P, &Q, &R), srand(P + Q + R);
    	rx = dis(B, A) / P, ry = dis(D, C) / Q;
    	if (!rx) P1.x = P1.y = 0; else P1 = P1 / rx;//特判传送带是一个点的情况
    	if (!ry) P2.x = P2.y = 0; else P2 = P2 / ry;
    	for (int i = 1; i <= cnt; i += 1)//随机撒点
    	{
    		b[i].x = b[i].pbx = Rand() * rx, b[i].y = b[i].pby = Rand() * ry;
    		b[i].f = b[i].pb = f(b[i].x, b[i].y);
    		b[i].vx = b[i].vy = 0;
    		if (gb > b[i].pb) gbx = b[i].pbx, gby = b[i].pby, gb = b[i].pb;
    	}
    	for (int p = 1; p <= 900; p += 1, w *= 0.99)//迭代900次,0.99的900次方已经对答案无影响
    		for (int i = 1; i <= cnt; i += 1)
    			if (Update(i), gb > b[i].pb)
    				gbx = b[i].pbx, gby = b[i].pby, gb = b[i].pb;//更新全局最优解
    	printf("%.2f\n", gb);
    	return 0;
    }
    
    • 1

    信息

    ID
    1611
    时间
    1000ms
    内存
    125MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者