三分,哈希, 模拟退火
I .三分
三分,顾名思义, 将一个区间分成三段(需要平均分), 以类似二分的处理方式判断答案在那个区间。
接着继续枚举, 直到确定结果。
三分一样需要单调性, 但单调性与二分有所不同。
二分需要满足数据严格“ 递增 ”或“ 递减 ”。而三分只需要满足数据是一个单峰或单谷函数(如图就是一个单谷函数)
接下来, 我
义图中左黄点为L(和二分相当的左端点), 右黄点为 R(右端点), 右蓝点为Rmid, 左蓝点为Lmid。
则绿点为所求答案。
那么, Rmid 和 Lmid 怎么求呢。
非常简单, (2 * l + r) / 3 = Lmid , (2 * r + l ) / 3 = Rmid。这里不证明
接着是模板
#include<bits/stdc++.h>
using namespace std;
int T;
int n;
int a[10005], b[10005], c[10005];
double s(double x)
{double ans = -1e9;for(int i = 1; i <= n; i++)ans = max(ans, a[i]*1.0*x*x + b[i]*1.0*x + c[i]*1.0);return ans;
}
int main()
{cin >> T;while(T--){cin >> n;for(int i = 1; i <= n; i++)cin >> a[i] >> b[i] >> c[i];double l = 0, r = 1000;while(r - l > 0.000000001){double mid1 = (l*2+r)/3;double mid2 = (l+r*2)/3;if(s(mid1) < s(mid2))r = mid2;elsel = mid1;}printf("%.4f\n", s(l));}
}
上述代码就是关于求一个单峰(单谷)的函数的解。
由此可见,三分可以解决数据(可能性)上升(下降)再下降(上升)的问题。
(其实就是二分变种)
II.哈希
哈希是指将字符串的状态表示为数字的算法,以用来状态压缩
普遍的, 我们将这个字符每一位都当作一个N进制数,并mod一个数(因为数可能很大)
int n; //进制
int p; //模数
string a;//待处理字符串
int ans;// 处理后的数字
for(int i = 0; i < p,size(); i++)ans = (ans + (p[i]-'a') * pow(n, i)) % p;//p[i] - 'a'指将字符处理成数字
(自然,n, p 甚至是每个字符表示的数字都是可以改变的)
这样,每个字符串(也就是状态)的比较就变成了O(1) 的复杂度。
但是,我们会发现一个问题 a%p 可能等于 b%p (b = a * x )。这时,a != b,但状态结果一样。这就是哈希冲突
为了尽量避免哈希冲突,我们可以将模数调大,并将模数改为质数
III.模拟退火(SA)
一种很玄的算法,我不希望在考场上遇见。
模拟退火,指模拟物理上退火的过程
首先, 要有一个温度变量T,当T变小时,答案跳动的幅度就会变得更小。
我们会用随机函数来随机出下一步的答案(即答案跳动)
如果答案更优,直接接受。如果答案更劣,则有
$$
P(\Delta E) = \begin{cases}
{1 , \hspace{1.1cm} S'\ \ is \ \ better \ \ than \ \ S} \
{e^{\frac{-\Delta E}{t}}},\hspace{1.1cm}ohters\
\end {cases}
$$
e 什么意思不用管,只知道代码就行了(如下)
exp(-d / t) *RAND_MAX > rand()//答案更劣时
整体伪代码如下
int t = 2000//温度(答案变动幅度)
int ans; //答案(暂时)
while(t > 1e-15)//1e-15可自定
{x = ansx + (rand() * 2 - RAND_MAX) * t;//括号内按题意自定d = pd(x)//判断答案更优或更劣if(d < 0)//更优ansx = x;//更新答案else if(exp(-d / t) * RAND_MAX > rand())//更劣有概率接受ansx = x;t *= 0.999//自定
}
由于有随机数的存在,所以答案不一定准确,需要调整参数,慢慢尝试。
(en, 有概率AC)
究极玄学算法