Description
给出一个系列,要求出所有长度为k (1<k<=n) 的序列中,最大值减去最小值的最小值,要求输出答案与正确答案相对误差均不超过 5%
Solution
听CTY说这题一眼看去要用近似算法,我不会(实际上也不用)。
因为答案有一定的浮动,所有考虑一下从答案入手,
枚举k,表示当前做到绝对值差为k,再
O(n)
扫一遍,即可知道适用k的区间最长是多少,更新答案。
我们发现,题目允许答案浮动,所有k不用枚举每一个数,只要枚举1.05的指数即可,即
0,1.050,1.051,1.052,1.053...
,
发现
1.05300=2273996>106
,可以过!!!
PS:如 f(i) 单调,并且可以知道 f(i)>k 的第一个i在哪,即可套用这个近似算法。
复杂度: O(nlog2(V))
优化:
1. 用RMQ加log2预处理可以O(1)得出区间最值
2. 可以直接用上一轮的答案*1.05
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<cstring>
#define foi(i,a,b) for(i=a;i<=b;i++)
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=100050,INF=2147483640;
int read(int &n)
{
char ch=' ';int q=0,w=1;
for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
if(ch=='-')w=-1,ch=getchar();
for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int n;
int a[N],er[18];
int mi[N*2][18],mx[N*2][18];
int LG[N];
int min(int q,int w){return q<w?q:w;}
int max(int q,int w){return q>w?q:w;}
int MIN(int l,int r){return min(mi[l][LG[r-l+1]],mi[r-er[LG[r-l+1]]+1][LG[r-l+1]]);}
int MAX(int l,int r){return max(mx[l][LG[r-l+1]],mx[r-er[LG[r-l+1]]+1][LG[r-l+1]]);}
int main()
{
int q=INF,e;double w;
er[0]=1;fo(i,1,17)er[i]=er[i-1]*2;
memset(mi,127,sizeof(mi));
read(n);mi[1][0]=mx[1][0]=read(a[1]);
fo(i,2,n)q=min(q,abs(a[i-1]-read(a[i]))),mi[i][0]=mx[i][0]=a[i];
fo(i,1,n)LG[i]=log2(i);
fo(j,1,LG[n]+1)fo(i,1,n)mi[i][j]=min(mi[i][j-1],mi[i+er[j-1]][j-1]),mx[i][j]=max(mx[i][j-1],mx[i+er[j-1]][j-1]);
w=q;printf("%d\n",q);e=0;
for(int I=3;I<=n;)
{
w*=1.05;q=0;
int i=1;
fo(j,2,n)
{
while(MAX(i,j)-MIN(i,j)>w)i++;
q=max(q,j-i+1);
}
fo(j,I,q)printf("%d\n",int(w));
I=max(I,q+1);
if(w<1)w=1;
}
return 0;
}