BZOJ_4476_[Jsoi2015]送礼物_01分数规划+单调队列
BZOJ_4476_[Jsoi2015]送礼物_01分数规划+单调队列
Description
JYY和CX的结婚纪念日即将到来,JYY来到萌萌开的礼品店选购纪念礼物。
萌萌的礼品店很神奇,所有出售的礼物都按照特定的顺序都排成一列,而且相邻
的礼物之间有一种神秘的美感。于是,JYY决定从中挑选连续的一些礼物,但究
竟选哪些呢?
【问题描述】
假设礼品店一共有N件礼物排成一列,每件礼物都有它的美观度。排在第i
1< =i< =N个位置的礼物美观度为正整数Ai,。JYY决定选出其中连续的一段,
即编号为礼物i,i+1,…,j-1,j的礼物。选出这些礼物的美观程度定义为
(M(i,j)-m(i,j))/(j-i+k)
其中M(i,j)表示max{Ai,Ai+1....Aj},m(i,j)表示min{Ai,Ai+1....Aj},K为给定的正整数。
由于不能显得太小气,所以JYY所选礼物的件数最少为L件;同时,选得太
多也不好拿,因此礼物最多选R件。JYY应该如何选择,才能得到最大的美观程
度?由于礼物实在太多挑花眼,JYY打算把这个问题交给会编程的你。
Input
本题每个测试点有多组数据。输入第一行包含一个正整数T(T< =10),表示
有T组数据。
每组数据包含两行,第一行四个非负整数N,K,L,R(2< =L< =R< =N。第二行
包含N个正整数,依次表示A1,A2....An,(Ai< =10^8),N,K< = 50,000
Output
输出T行,每行一个非负实数,依次对应每组数据的答案,数据保证答案不
会超过10^3。输出四舍五入保留4位小数。
Sample Input
1
5 1 2 4
1 2 3 4 5
5 1 2 4
1 2 3 4 5
Sample Output
0.7500
可以发现一定是最大值和最小值都在两端时最优,但可能长度超出限制。
答案可能由两部分组成:1.长度为L 2.最大值和最小值在两端。
第一种情况直接维护个单调栈即可。
第二种情况是个经典的01分数规划。
二分答案x
有$a[l]-a[r]>(r-l+k)*$x或$a[r]-a[l]>(r-l+k)*x$
分别求$(a[l]+l*x)-(a[r]+r*x)$和$(a[l]-l*x)-(a[r]-r*x)$的最大值,用单调队列求即可,多个log就过不去了。
代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stdlib.h>
using namespace std;
typedef double f2;
#define eps 1e-7
#define N 50050
int n,a[N],K,L,R,Q1[N],l1,r1,Q2[N],l2,r2,Q3[N],l3,r3;
f2 t[N];
int main() {
int T;
scanf("%d",&T);
while(T--) {
scanf("%d%d%d%d",&n,&K,&L,&R);
int i;
for(i=1;i<=n;i++) scanf("%d",&a[i]);
l1=r1=l2=r2=0;
for(i=1;i<L;i++) {
while(l1<r1&&a[Q1[r1-1]]>=a[i]) r1--;
while(l2<r2&&a[Q2[r2-1]]<=a[i]) r2--;
Q1[r1++]=Q2[r2++]=i;
}
f2 ans1=-1000;
for(i=L;i<=n;i++) {
while(l1<r1&&i-Q1[l1]>=L) l1++;
while(l2<r2&&i-Q2[l2]>=L) l2++;
while(l1<r1&&a[Q1[r1-1]]>=a[i]) r1--;
while(l2<r2&&a[Q2[r2-1]]<=a[i]) r2--;
Q1[r1++]=Q2[r2++]=i;
ans1=max(ans1,1.0*(a[Q2[l2]]-a[Q1[l1]])/(L-1+K));
}
f2 l=0,r=1000;
while(r-l>eps) {
f2 mid=(l+r)/2;
f2 re=-100000;
l3=r3=0;
for(i=1;i<=n;i++) t[i]=a[i]-i*mid;
for(i=L+1;i<=n;i++) {
while(l3<r3&&i-Q3[l3]>=R) l3++;
while(l3<r3&&t[Q3[r3-1]]>=t[i-L]) r3--;
Q3[r3++]=i-L; re=max(re,t[i]-t[Q3[l3]]);
}
l3=r3=0;
for(i=1;i<=n;i++) t[i]=a[i]+i*mid;
for(i=n-L;i;i--) {
while(l3<r3&&Q3[l3]-i>=R) l3++;
while(l3<r3&&t[Q3[r3-1]]>=t[i+L]) r3--;
Q3[r3++]=i+L; re=max(re,t[i]-t[Q3[l3]]);
}
if(re>=mid*K) l=mid;
else r=mid;
}
//printf("%.4f\n",ans1);
printf("%.4f\n",max(ans1,l));
}
}