jzoj 4017. 【雅礼联考DAY01】单调队列优化dp+01分数规划

Description

Konrad, Delfador 和 Kalenz 一行人又喜闻乐见地被追杀了。
他们面临的是一条有 N 个地点的路, 他们从 0 号地点出发, 要逃到 N 号地点去。每个地点的战斗都有一定的金币收入 Ai,也有一定的部队损失 Bi。
为了更好地逃生, Delfador 还弄到了一块传送宝石,这样一行人就能向后传送不超过 L 的距离。从一个地点传送到另一个地点时,Konrad 会选择路径上除起点外的地形指数 Ci 最大的地点进行战斗,地形指数相同时选择最靠后的。
作为优秀的领导者, Konrad 希望总金币收入与总部队损失的比值最大。

Input

第一行,两个整数 N, L。
接下来 N 行,每行两个整数,分别表示 Ai, Bi, Ci。

Output

一行,一个实数,表示答案。
答案请使用科学计数法输出,保留 9 位小数,具体参见输出样例。指数为 0 时,最后应当输出’0.000000000e+000’。

Sample Input

5 3
1 1 1
1 2 2
2 3 1
1 9 2
1 1 1

Sample Output

3.750000000e-001

Data Constraint

分析:
显然第一步就是分数规划了,转换为判定性问题,设 m i d 为当前的答案,如果mid是合法的,有 a b > m i d ,也就是每个点的权值变为 w i = a i m i d b i ,然后判断答案是否大于等于0。

然后暴力枚举位数,也就是指数,判断一下。再从1.0~9.999二分一下,再判断。

关键就是判断了,我们设 f [ i ] 为到 i 的最大权值,显然有,

f [ i ] = f [ j ] + w [ t ]

其中 i j <= L t 为区间 [ j , i ] c 值最大的位置。

显然区间 [ j , i ] 中的最大值满足单调性(不严格单调减)。所以可以用单调队列维护这个下降的最大值,每次加入当前点的 c [ i ] ,就把单调队列中小于等于 c [ i ] 的全部变为 c [ i ] ,跳跃代价变为当前点代价。然后我们发现,当有两个位置的跳跃代价相同,即 c [ i ] 相同,位置靠前且 f [ i ] 更小,显然这个点一定不优。也就是相同 c [ i ] 值也要满足单调减。每次把不在范围内的头弹掉,就可以实现转移,转移的位置为 c [ i ] 值相同中的最大值,对于每个 c [ i ] 值都有可能转移,取最大值即可。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long

const int maxn=1e5+7;
const double inf=1564918894981;

using namespace std;

double a[maxn],b[maxn],c[maxn],f[maxn];
double ans;
int n,L,e,h,t;

struct que{
    double x,c,w,num,last,next;
}q[maxn];

bool check(double x)
{   
    f[0]=0;
    h=1; t=1;
    q[1]=(que){0,0,0,0,0,0};        
    for (int i=1;i<=n;i++)
    {
        double w=a[i]-b[i]*x;
        f[i]=-inf;
        while (q[h].num+L<i)
        {
            if (q[h].next!=h+1) q[h+1].next=q[h].next;
            h++;
        }
        int z,g=h;
        for (int j=t;j>=h;j--)
        {
            if (q[j].c<=c[i]) q[j].c=c[i],q[j].w=w;
                         else {g=j+1; break;}
        }
        int last=q[g].last;
        q[g].last=0; q[g].next=0;
        z=g;        
        for (int k=g+1;k<=t;k++)
        {
            q[k].last=0; q[k].next=0;
            q[++z]=q[k];
            while ((q[z].x>=q[z-1].x) && (z>g)) q[z-1]=q[z],z--;
        }
        t=z;
        q[g].last=last;
        q[last].next=g;
        int j=h;
        while (j>0)
        {
            f[i]=max(f[i],q[j].x+q[j].w);
            j=q[j].next;
        }
        q[++t]=(que){f[i],0,0,i,t-1,0};
        q[g].next=t; q[t].last=g;
    }   
    if (f[n]>=0) return 1;
    else return 0; 
}

int main()
{   
    scanf("%d%d",&n,&L);
    for (int i=1;i<=n;i++) scanf("%lf%lf%lf",&a[i],&b[i],&c[i]);
    LL k=1e8;
    for (int i=1;i<=n;i++) a[i]=a[i]*k;             
    for (int i=-7;i<=7;i++)
    {
        for (int j=1;j<=n;j++)
        {
            if (i>0) b[j]*=10;
                else a[j]/=10;
        }                               
        if (!check(10)) {e=i; break;};
    }       
    double l=1,r=9.9999999999;
    while (r-l>1e-12)
    {
        double mid=(l+r)/2;
        if (check(mid)) l=mid,ans=mid;
                   else r=mid;
    }
    printf("%.9lf",ans);
    if (e<0) printf("e-00%d",-e);
        else printf("e+00%d",e);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81035248