【UOJ 测试】B. 【#245 UER #7】天路(近似算法+RMQ)

来源:互联网 发布:淘宝黄妹子美货假货 编辑:程序博客网 时间:2024/05/16 12:56

245. 【UER #7】天路
  隆冬将至,几天后跳蚤国便会迎来寒冬,这对于以血肉之躯和飞机搏斗的跳蚤们来说并不是件好事……然而在悠悠历史岁月中,跳蚤国早已有了应对严寒的应急措施方案!
  在跳蚤国王的带领下,跳蚤们准备启动天路热能塔 —— 红米 note7(红米 note7 为发烧而生)。这座热能塔高耸入云,直接穿出大气层从太空中直接吸收太阳光,垂直向下将热能送往跳蚤国各个角落。热能塔的制造工艺巧夺天工,被誉为“带来温暖的天路”。
  但是跳晚们为了让跳蚤们都因为天气寒冷赖在被子里不肯起床,在热能塔启动后一定会歇斯底里地进攻。跳蚤国高级间谍的情报显示,跳晚国计划将发射 n台三星 note7 向热能塔发起进攻。进攻将会按一定顺序进行,其中第 ii 次进攻的高度为 ai(1≤i≤n)。
  为了防止热能塔被炸毁,跳蚤国王特地派尛焱轟(一种新型交通工具,运载能力是小火车的三次幂)运送来了跳蚤们刚研制出不久的新型材料 —— Nokia1050。跳蚤们将会把 Nokia1050 装在热能塔上的某一连续的高度区间上以抵挡进攻。
  现在,跳蚤国王想在热能塔受损程度和材料消耗量之间进行取舍。所以对于每个 2≤k≤n,跳蚤国王想知道整个攻击过程中如果想让 Nokia1050 在某一时段至少挡住连续 k次攻击,那么安装 Nokia1050 的高度区间的长度至少是多少。其中,若高度区间为 [l,r],则长度为 r−l。
  事实上,间谍的消息也不见得会多么靠谱,所以跳蚤国王仅想知道一个不那么准确的答案。具体来说:如果对于每个 k你输出的答案 ck与标准答案 c^k 的相对误差均不超过 5%,则算作正确。即:∣ck−c^k∣≤5%⋅c^k
输入格式
 第一行一个正整数 n,保证 n≥2。
 第二行 n个正整数 a1,…,an,按顺序给出每次进攻时三星 note7 的高度。
输出格式
 输出 n−1行,其中第 k−1行表示至少抵挡连续 k次攻击时所需的最短高度区间长度。(2≤k≤n)
 因为十分重要所以说两遍,如果对于每个 kk 你输出的答案 ck与标准答案 c^k的相对误差均不超过 5%,则算作正确。即:∣ck−c^k∣≤5%⋅c^k
样例一
 input
  4
   1 7 5 2
  output
   2
   5
   6
  explanation
   当 k=2时,最优高度区间为 [5,7];
   当 k=3时,最优高度区间为 [2,7];
   当 k=4 时,最优高度区间为 [1,7];
   注意 k=2时不能选择高度区间 [1,2],虽然能够拦截下第 1次和第 4次攻击,但这两次攻击并不连续。
样例二
  input
   10
   26 723 970 13 422 968 875 329 234 983
  output
   93
   546
   639
   734
   749
   957
   957
   957
   970
  explanation
   样例输出给出的为准确答案,注意下面的输出也是可接受的:
     93
     540
     630
     730
     740
     960 
     960
     960
     970

【题解】【近似算法+RMQ】

【算法一】【线段树或RMQ维护区间最大最小值,暴力求解。期望得分:50分】

【正解来袭!】

【这是一个求解近似值的题,从前貌似没做过。。。】

【其实,我们可以从求准确值开始考虑:那么,即上面50分的做法,枚举长度暴力求解。但我们会发现:近似的是答案而不是区间长度,这样做根本没法近似。所以,我们考虑枚举结果,每次查找当前结果所能满足的最长距离即可。这样我们就可以近似了:枚举时答案的跨越长度是1.05(至于为什么是1.05,我表示我也不清楚。。。hxy说应该是根据允许5%的误差范围选的,好像很有道理!)。】

【查找每次的距离时,我们可以用两个指针t1、t2,如果当前区间[t1,t2]的答案还小于当前的答案,那么,我们将t2后移,否则将t1后移。在判断区间[t1,t2]的答案时,我们用RMQ来查询。】

【RMQ预处理每个区间的最大值和最小值,可以做到O(1)查询】

#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int maxn[100010][20],minn[100010][20],mi[100010];int ans[100010],n,a[100010];inline void pre(){for(int j=1;j<20;++j) for(int i=1;i<=n;++i)  if(i+(1<<(j-1))<=n)   {   maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);   minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);   }for(int i=1;i<=n;++i) { int k=0; while((1<<k)<=i) ++k; mi[i]=k-1; }}inline int get_cha(int l,int r){int ans1=0,ans2=0,k=mi[r-l+1];ans1=max(maxn[l][k],maxn[r-(1<<k)+1][k]);ans2=min(minn[l][k],minn[r-(1<<k)+1][k]);return (ans1-ans2);}inline int find(int x){int t1=1,t2=1,len=0;while(t1<=n&&t2<=n) { int sum=get_cha(t1,t2); if(sum<=x)   {  len=max(len,t2-t1+1);  if(t2<n) t2++;   else t1++;  } else  if(t1<n) t1++;   else t2++; }return len;}int main(){int i,j;scanf("%d",&n);for(i=1;i<=n;++i)scanf("%d",&a[i]),maxn[i][0]=minn[i][0]=a[i];pre();memset(ans,127,sizeof(ans));int m=get_cha(1,n),len=find(0);ans[len]=0;for(double i=1;i<=m*1.05;i*=1.05) { int x=floor(i); int sum=find(x); ans[sum]=min(ans[sum],x); }for(i=n-1;i>1;--i) ans[i]=min(ans[i],ans[i+1]);for(i=2;i<=n;++i) printf("%d\n",ans[i]);return 0; } 

[附UOJ题解]



[反正我没大看懂。。。]


0 0