/* 头部 */ #header { position: relative; height: 280px; margin: 0; background: #020031; background: -moz-linear-gradient(45deg,#020031 0,#6d3353 100%); background: -webkit-gradient(linear,left bottom,right top,color-stop(0%,#020031),color-stop(100%,#6d3353)); background: -webkit-linear-gradient(45deg,#020031 0,#6d3353 100%); background: -o-linear-gradient(45deg,#020031 0,#6d3353 100%); background: -ms-linear-gradient(45deg,#020031 0,#6d3353 100%); background: linear-gradient(45deg,#020031 0,#6d3353 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#020031', endColorstr='#6d3353', GradientType=1); -webkit-box-shadow: inset 0 3px 7px rgba(0,0,0,.2),inset 0 -3px 7px rgba(0,0,0,.2); -moz-box-shadow: inset 0 3px 7px rgba(0,0,0,.2),inset 0 -3px 7px rgba(0,0,0,.2); box-shadow: inset 0 3px 7px rgba(0,0,0,.2),inset 0 -3px 7px rgba(0,0,0,.2); }

民科吧编程赛 题解

试卷地址

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

1.选择题

【题目简介】

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

【思路&解答】

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

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

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

abf(x)dx(ba)(f(a)+f(b)+4f(a+b2)6

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

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

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

代码如下:

#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); }

填空题

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

img

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

可以通过公式 limΔx0sinx=x 和二倍角公式 sin(α+β)=sinαcosβ+sinβcosα 进行计算。

函数计算代码如下:

//没有加诱导公式 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<ba,f(x)<0;$$x>ba,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^{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; }

__EOF__

本文作者小红
本文链接https://www.cnblogs.com/little-red/p/13394404.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   是小红吖  阅读(401)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示