洛谷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;
}
posted @ 2025-07-21 20:44  司马只因锥  阅读(25)  评论(0)    收藏  举报