洛谷P1001题解
洛谷P1001题解
tag:爬山算法,三分
蒟蒻刚刚学了爬山算法,今天就来做一道爬山算法好题。
初看题目一看没思路呀,首先构造函数\(f(x)=x-a-b\),当\(x-a-b=0\)时的\(x\)就是答案,但爬山算法解决的是单峰函数,然而构造函数发现这个题的函数却是单调的,我们怎么把模板套上去呢?
这显然需要一种转换,根据我们小学二年级学过的知识,我们注意到\(\int (kx+b)\,dx=\frac{k}{2}x^2+bx+C\),其中\(C\)为常数,那么这个题带入一下可得
\[\int f(x)\,dx=\frac{1}{2}x^2-(a+b)x+C
\]
显然\(C\)是多少是不影响函数单调性的,因此我们设\(C\)为\(0\)。
由于积分和求导互为逆运算,因此我们可以知道这个函数的导函数就是我们构造的单调函数,也就是说当这个函数达到极值点时,原函数的函数值为\(0\)。
注意到新函数是二次函数,结合二次函数图像可知这是单峰函数,且开口向上,那么我们就尽量爬向低处,于是就可以愉快地爬山啦~
等等!但我们注意到这题\(|a|,|b|<10^9\),根据公式计算一下发现在极端情况下的函数值会炸double!
但没关系,我们可以把\(a,b\)对\(100\)(或者其他数字,不能太大因为要爬山)取余(不是取模!否则负数会变成正数),然后把答案再加上我们减去的那一部分即可。
我采用了多次重置温度去爬山,其他写法不知道对不对反正爬山算法不就是写个调参吗。
code
#include <bits/stdc++.h>
//#define int int64_t
//#define int __int128
//#define MOD (1000000007)
#define eps (0.5)
#define endl '\n'
#define debug_endl cout<<endl;
#define debug cout<<"debug"<<endl;
using namespace std;
const int LukyNum=0x0d000721;
int a,b,tmp;
double ans;
double delta;
double A,B;
double f(double x){
return A*powf(x,2)+B*x;
}
void modify(double t){//爬山
double nexright=f(ans+t),nexleft=f(ans-t);//向前或者向后
double nowf=f(ans);
if(ans+t<2e9&&nexright<=nowf){//选择更低的解
delta=t;
}
else if(ans-t>-2e9&&nexleft<nowf){
delta=-t;
}
//如果都比now大说明都跳太多了,不修改
}
double solveEps(double x){
return (fabs(x)<eps?0:x);
}
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>a>>b;
tmp=a/100+b/100;
a%=100,b%=100;
A=0.5;//二次项系数
B=-(a+b);//一次项系数
ans=0;
for(int i=1;i<=10;++i){
for(double t=2e2;t>=0.001;t*=0.999995){//温度递减,我们注意到这个题的答案区间非常大,温度>1很容易爆炸,因此以极小的温度起始
modify(t);
ans+=delta;//修改当前答案
}
}
printf("%.0lf",solveEps(ans)+tmp*100);//这里的四舍五入如果是负数取0会出现输出-0的情况,我们特判一下
return 0;
}
因为是单峰函数我们还可以三分,也提供个三分写法
#include <bits/stdc++.h>
//#define int int64_t
//#define int __int128
//#define MOD (1000000007)
#define eps (0.1)
#define endl '\n'
#define debug_endl cout<<endl;
#define debug cout<<"debug"<<endl;
using namespace std;
int a,b;
double ans;
double delta;
double A,B;
double f(double x){
return A*powf(x,2)+B*x;
}
double solveEps(double x){
return (fabs(x)<eps?0:x);
}
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
cin>>a>>b;
delta=a/1000+b/1000;
a%=1000,b%=1000;
A=0.5;//二次项系数
B=-(a+b);//一次项系数
ans=0;
double l=-2e3,r=2e3;
while(r-l>eps){
double mid=(l+r)/2;
if(f(mid-eps)<f(mid)){
r=mid;
}
else{
l=mid;
}
}
ans=(l+r)/2;
printf("%.0lf",solveEps(ans)+delta*1000);//这里的四舍五入如果是负数取0会出现输出-0的情况,我们特判一下
return 0;
}

浙公网安备 33010602011771号