Codeforces.739E.Gosha is hunting(DP 带权二分)

题目链接

\(Description\)

有n只精灵,两种精灵球,每种球能捕捉到第i只精灵的概率已知。求用A个低级球和B个高级球能捕捉到精灵数的最大期望。

\(Solution\)

设f[i][a][b]表示前i只用了a个低级球,b个高级球的最大期望。转移时四种情况显然。复杂度\(\mathcal O(nAB)\)
随着某种球可使用数的增多,f应是凸函数,即增长越来越慢。而且两种球都满足这个性质。
于是可以wqs二分套wqs二分了。。没有个数限制取个max记一下个数就可以了。复杂度\(\mathcal O(nlog^2n)\)
误差<=1e-4,因为最后要*A/B,所以eps应是1e-8。
必须取r感觉不太懂。。

总结(个人理解):对于有着次数/段数之类的限制,可以使用带权二分来消掉这一限制,从而可以进行简单的快速DP。

//46ms    0KB
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define eps (1e-12)
const int N=2003;

int n,A,B,na[N],nb[N];
double pa[N],pb[N],Ans;

void Solve(double ca,double cb)
{
    na[0]=nb[0]=0;
    double las=0, now;
    for(int i=1; i<=n; ++i, las=now)
    {
        now=las, na[i]=na[i-1], nb[i]=nb[i-1];
        if(las+pa[i]-ca>now) now=las+pa[i]-ca, na[i]=na[i-1]+1;
        if(las+pb[i]-cb>now) now=las+pb[i]-cb, nb[i]=nb[i-1]+1, na[i]=na[i-1];
        if(las+pa[i]+pb[i]-pa[i]*pb[i]-ca-cb>now)//1-(1-pa)(1-pb)
            now=las+pa[i]+pb[i]-pa[i]*pb[i]-ca-cb, na[i]=na[i-1]+1, nb[i]=nb[i-1]+1;
    }
    Ans=now;
}

int main()
{
    scanf("%d%d%d",&n,&A,&B);
    for(int i=1; i<=n; ++i) scanf("%lf",&pa[i]);
    for(int i=1; i<=n; ++i) scanf("%lf",&pb[i]);
    double l1=0,r1=1,mid1,l2,r2,mid2;//每个球0/1的权值就可以了啊 
    while(r1>=l1+eps)
    {
        mid1=(l1+r1)*0.5;
        l2=0, r2=1;
        while(r2>=l2+eps)
        {
            if(Solve(mid1,mid2=(l2+r2)*0.5),nb[n]>B) l2=mid2;
            else r2=mid2;
        }
        if(Solve(mid1,r2),na[n]>A) l1=mid1;//最优可行的是r2?反正不是l2。。
        else r1=mid1;
    }
    Solve(r1,r2);//最后Check一遍r。。
    printf("%.5lf",Ans+A*r1+B*r2);

    return 0;
}

猜你喜欢

转载自www.cnblogs.com/SovietPower/p/9163792.html