• 民科吧编程赛 题解


    试卷地址

    评:没几个人做呀,是因为我的知名度不如小梦,还是出的太难了?

    1.选择题

    题目简介

    只允许使用四则运算,求出任意正数 $ x $ 的自然对数 $ ln (x) $

    思路&解答】

    由于无法使用 $ ln $ 函数,所以普通的方法,如二分法,牛顿法等依托原函数的迭代法无法使用。

    考虑到 $ ln (x)$ 的特殊性质 $ ln (x) ' =  frac{1}{x} $ ,可以使用积分方法求出。

     自适应辛普森法(推导过程不再给出)

    $  int  _a ^b f(x) d x approx dfrac{(b-a)(f(a)+f(b)+4f( frac{a+b}{2} )}{6} $

    递归计算精度是否满足要求,具体过程不再给出

    值得点出的是, 当 $ x>1 $时,要 $ x = 1$ 从开始进行正积分,当 $ 0<x<1 $ 时,要从 $x=1$ 开始进行负积分,否则误差会很大。

    注意不要使用 $ln(ab)= ln a + ln b$ 和近似公式来完成这道题。因为为了用这个你可能还得写上上百行代码,而且亲测使用近似公式的话即使在取到也会有的偏差。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const double eps=1e-6;
    long double a,l2=1,r;
    double f(double x){
        return  1/x;
    }
    double simps(double a,double b){
        double c=(a+b)/2.0;
        return (f(a)+f(b)+4.0*f(c))*(b-a)/6.0;
    }
    double zsy(double a,double b,double eps){
        double c=(a+b)/2.0;
        double mid=simps(a,b),L=simps(a,c),R=simps(c,b);
        if(fabs(L+R-mid)<=15*eps)return L+R+(L+R-mid)/15.0;
        return zsy(a,c,eps/2.0)+zsy(c,b,eps/2.0);
    }
    int main(){
        cin>>r;
        if(r<1)cout<<fixed<<setprecision(6)<<"-"<<zsy(r,1,eps);
        else cout<<fixed<<setprecision(6)<<zsy(l2,r,eps);
    }

     

    填空题

     

    这道题是被骂的最狠的一道。因为导数太毒瘤。

    此题难点在于 $ sin x $ 函数的四则运算表达和函数最值的计算。

    可以通过公式 $ lim limits_{Delta x o 0} sin{x} = x $ 和二倍角公式 $ sin( alpha + eta )= sin alpha cos eta +sin eta cos alpha $ 进行计算。

    函数计算代码如下:

    //没有加诱导公式
    const double init=0.0001;
    const double pi=3.14159265;
    double sinx[15]={0.0001,0.0002,0.0004,0.0008,0.0016,0.00319999,0.00639996,0.01279965,0.02559720,0.05117763,0.10222114,0.20337134,0.39824245,0.73059982,0.99771574};
    long double getsin(long double x){
        if(x<=init)return x;
        int bei=14;
        while(bei--){
            if(x==(init*(1<<bei)))return sinx[bei];
            if(x>(init*(1<<bei))){
                long double tmpS=getsin(x-init*(1<<bei));
                long double tmpC=sqrt(1-tmpS*tmpS);
                return tmpS*sqrt(1-sinx[bei]*sinx[bei])+tmpC*sinx[bei];
            }
        }
    }
    

     

    开平方函数四则运算计算方法不再赘述。

     

    观察函数,下面恒 $>0$,上面的函数很容易看出来 $0<x< frac{b}{a} , f(x)<0 ;$ $x> frac{b}{a} , f(x)>0$

     

    再看分母,当$x$很大时函数基本为0,所以可以使用爬山法求最值。

     

    爬山法:

    随机取一点,判断该点处是上坡还是下坡,上坡则将该点往前进,下坡则往后退,多次迭代后可求出最值。

    为了防止出现该点卡在题图中给出的双峰函数比较小的峰上,可以多次随机取点得到最大值。

    本题当然也可使用快速傅里叶变换,但很显然为了这么简单一道题用这么复杂的算法很不值得……

    注意玄学错误:l和r会卡在两个相对山峰上出不来,这时候得判一判

    不过还是建议使用一个变量

     

    双变量代码

    #include<bits/stdc++.h>
    using namespace std;
    double a,b,ans,delta=1e-6;
    long double f(double x) {
    	return (a*x*x*x-b*x*x)/(exp(x*x-sin(x)-1));
    }
    double getdel(double x) {
    	return f(x+delta)-f(x);
    }
    void pa() {
    	double l=b/a;
    	double r=(double)rand()/100;
    	while(r-l>1e-6) {
    		double step=(r-l)/100;
    		if(getdel(l)<1e-8)l-=step; //注意玄学错误:l和r会卡在两个相对山峰上出不来,这时候得判一判
    		if(getdel(r)>1e-8)r+=step;//不过还是建议使用一个变量
    		if(getdel(l)>1e-8)l+=step;
    		if(getdel(r)<1e-8)r-=step;
    	}
    	ans= ans>f(l)? ans:f(l);
    }
    int main() {
    	srand(time(NULL));
    	cin>>a>>b;
    	for(register int i=3; i; i--)pa();
    	cout<<fixed<<setprecision(6)<<ans;
    	return 0;
    }
    

     

    单变量代码(还没优化……容易被卡)

    #include<bits/stdc++.h>
    using namespace std;
    double a,b,ans,delta=1e-6;
    long double f(double x) {
    	return (a*x*x*x-b*x*x)/(exp(x*x-sin(x)-1));
    }
    double getdel(double x) {
    	return f(x+delta)-f(x);
    }
    void pa() {
    	double as=(double)rand()/1000;
    	double step=1;
    	double t=100;
    	while(abs(t)>1e-7) {
    		t=getdel(as);
    		step=t;
    		if(getdel(as)<0)as-=step;
    		if(getdel(as)>0)as+=step;
    	}
    	ans= ans>f(as)? ans:f(as);
    }
    int main() {
    	srand(time(NULL));
    	cin>>a>>b;
    	for(register int i=10; i; i--)pa();
    	cout<<fixed<<setprecision(6)<<ans;
    	return 0;
    }
    

      

     

    3.解答题

     

    这次民科吧大赛出的最失败的一道题,本来想的只能用模拟退火写的题被左茂雄老师一个质心公式秒掉……

    题目灵感源于: [JSOI2004]平衡点 / 吊打XXX

     

    1.质心公式法:

     

    $ x_{ sigma} = dfrac { sum _{ i=1 } ^ { n } x_i m_i}{ sum_ {i=1}^{n}m_i} $

    $ y_{ sigma} = dfrac { sum _{ i=1 }^{n}y_i m_i}{sum_{i=1}^{n}m_i} $

     

    2.模拟退火:

    1.随机出来一个质心并通过该质心得到各质点矢径

    2.计算各质点对质心的贡献向量并相加。

    3.向着加和方向走一点点

    4.迭代回到第二步,直到无路可走,该点为质心。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<ctime>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<iomanip>
    using namespace std;
    struct med{
        int x;
        int y;
        int w;
    };
    med a[1005];
    double ansx,ansy,ans=1e18,t;
    int n,sx,sy;
    const double delta=0.993;
    double calce(double x,double y){
        double ret=0;
        for(register int i=1;i<=n;i++){
            double deltax=x-a[i].x,deltay=y-a[i].y;
            ret+=sqrt(deltax*deltax+deltay*deltay)*a[i].w;
        }
        return ret;
    }
    void sa(){
        double x=ansx,y=ansy;
        t=3000;
        while(t>1e-17){
            double xx=x+((rand()<<1)-RAND_MAX)*t;
            double yy=y+((rand()<<1)-RAND_MAX)*t;
            double now=calce(xx,yy);
            double Delta=now-ans;
            if(Delta<0){
                x=xx,y=yy;
                ansx=x,ansy=y,ans=now;
            }
            else if(exp(-Delta/t)*RAND_MAX>rand())x=xx,y=yy;
            t*=delta;
        }
    }
    void solve(){
        ansx=(double)sx/n,ansy=(double)sy/n;
         while(clock()<0.7)SA();
    }
    int main(){
        ios::sync_with_stdio(false);
        srand(time(NULL));
        cin>>n;
        for(register int i=1;i<=n;i++){
            cin>>a[i].x>>a[i].y>>a[i].w;
        }
        solve();
        cout<<fixed<<setprecision(3)<<ansx<<" ";
        cout<<fixed<<setprecision(3)<<ansy;
        return 0;
    }
  • 相关阅读:
    pdf文件怎么转换成word文档
    数据库的发展历程
    数据库的三级模式
    数据库概述
    时间序列的自回归模型—从线性代数的角度来看
    数据清洗
    sql commit的三种方式
    数据库标准语言SQL((Structured Query Language)
    正则化推导转载
    leetcode刷题
  • 原文地址:https://www.cnblogs.com/little-red/p/13394404.html
Copyright © 2020-2023  润新知