Problem B. Code-Eat Switcher Google Kick Start Round E 2019

题意:每天分为S个time slots, Umon每个time slot可以可以花一部分时间eating,花剩下的时间coding。整个time slot i都用来eating的收益是E[i],都用来coding的收益是C[i],要求分配每个time slot的coding,eating百分比,保证每一天所有time slot的coding, eating收益之和不低于某个lower bound。

一个standard的LP问题,然而Google肯定不会考察解LP。

考虑最简单的case,如果S=1,我们只需要保证f*C[0]>=A,(1-f)*E[0]>=B。我们可以先保证满足coding的constraint,算出f的最小值,再check eating的constraint是否满足。

如果S=2,我们需要满足f0*C[0]+f1*C[1]>=A,(1-f0)*E[0]+(1-f1)*C[1]>=B。注意因为A和B都是整数,我们只要枚举time slot 0用来coding的Unit就可以了。如果time slot 0用来coding的unit是个fractional,那么LHS一定是个fractional,所以还可以向下round(将unit变成整数)使得LHS是整数。

以下这种case是不存在的:

 CE
t=038
t-1610
f0=0.51.54
f1=0.535
 A=4.5B=9

然而当时并没有想出来这一题是暴力。我开始以为是搜索找到f0,再fix f0找f1(转化成S=1的问题)。感觉会有二分搜索的特性:f0太小,A成立,B不成立;f0逐渐增加,到达某个区间后A,B都成立,f0太大,A不成立,B不成立。如果A,B都不成立,那么增加or减小f0都会使得至少一个constraint的LHS更差,所以解不存在。但是这种solution一直WA,可能单调性不成立吧。>_<

对于large input很有迷惑性。原始的solution space是每个time slot都分成两部分,但是分析一下发现只要将一个time slot分成两部分。

因为只有两个constraint,我们可以考虑一个strategy满足constraint A,同时尽量满足constraint B。对于每个time slot,如果多分一个unit给eating,那么coding减少的unit就是C[i]/E[i],所以应该优先选择C[i]/E[i]比较小的time slot给eating。所以可以按照C[i]/E[i] greedily排序,前半部分的time slots给eating,后半部分的time slots给coding。因为每组E,C都有D个query,可以通过前缀和,后缀和避免重复计算。另外可以用二分查找找到应该split的time slot。

为什么只split one time slot就是对的: 假设可以split two time slots t0, t1, t0<t1 after sorting。for eating t0获取e0, t1获取e1,那么把e1合并到t0,减少的coding unit一定更小。

#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<string>
#include<cmath>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;

const int maxn=100010;
const double eps=1e-9;
int T;
int D;
int S;
int C[maxn];
int E[maxn];
int A[maxn];
int B[maxn];
string ans;
const double maxrange=1.0;
const double minrange=0.0;
double CEratio[maxn];//C[i]/E[i]
int presumE[maxn];
int suffixsumC[maxn];
class node
{
public:
    int coding;
    int eating;
    double ceratio;//coding/eating
    int index;
    node()
    {
        coding=0;
        eating=0;
        ceratio=0;
        index=0;
    }
    node(int c,int e,int idx)
    {
        coding=c;
        eating=e;
        index=idx;
        if(eating!=0)
        {
            ceratio=1.0*coding/eating;
        }
        else
        {
            ceratio=100000.0;
        }
    }
};
node arr[maxn];
int trisearchinner(const double f1,int idx)//given f1
{
    double f2=1.0*(A[idx]-f1*C[0])/C[1];
    f2=min(f2,1.0);
    f2=max(f2,0.0);
    double resa=f1*C[0]+f2*C[1];
    double resb=(1-f1)*E[0]+(1-f2)*E[1];
//    cout<<"f1 "<<f1<<" f2 "<<f2<<endl;
    if(resa>=A[idx]&&resb>=B[idx])
    {
        return 0;
    }
    else if(resa<A[idx]&&resb>=B[idx])
    {
        return 1;
    }
    else if(resa>=A[idx]&&resb<B[idx])
    {
        return 2;
    }
    else
    {
        return 3;
    }
}
bool trisearchouter(int idx)//search along f1
{
    for(int c=0;c<=C[0];c++)
    {
        if(c+C[1]<A[idx])
        {
            continue;
        }
        else
        {
            double f0=1.0*c/C[0];
            double f1=1.0*(A[idx]-c)/C[1];
            f1=max(f1,0.0);
            if(E[0]*(1-f0)+E[1]*(1-f1)>=B[idx])
            {
                return true;
            }
        }
    }
    return false;
    double left=minrange-0.01;
    double right=maxrange+0.01;
    double mid=(left+right)/2;
    while(left<right)//+eps
    {
        mid=(left+right)/2;
        int res0=trisearchinner(mid,idx);
//        cout<<mid<<" "<<res0<<endl;
        if(res0==0)
        {
            return true;
        }
        if(res0==3)
        {
            return false;
        }
        if(res0==1)//A fails
        {
            left=mid;
        }
        if(res0==2)//B fails
        {
            right=mid;
        }
    }
//    return false;
    if(left>=0&&left<=1)
    {
        int res0=trisearchinner(left,idx);
        if(res0==0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    if(right>=0&&right<=1)
    {
        int res1=trisearchinner(right,idx);
        if(res1==0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    return false;
}
bool trisearchsingle(int idx)//when S==1
{
    double f1=1.0*A[idx]/C[0];
    double res0=(1-f1)*E[0];
    if(res0>=B[idx])
    {
        return true;
    }
    return false;
}
bool cmp(node a,node b)
{
    return a.ceratio<b.ceratio;
}
bool split_single_day(int timeslot,int idx)//split this timeslot into two parts, one for coding, one for eating
{
    int remain=presumE[timeslot]-B[idx];
//    if(remain>arr[timeslot].eating)//already computed in previous time slot
//    {
//        return false;
//    }
    double f=1.0*remain/arr[timeslot].eating;//fraction to coding
    double remain_coding=f*arr[timeslot].coding;
    if(remain_coding+suffixsumC[timeslot+1]>=A[idx])
    {
        return true;
    }
    return false;

}
int main()
{
    freopen("input.txt","r",stdin);
    cin>>T;
    for(int ca=1;ca<=T;ca++)
    {
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
        memset(C,0,sizeof(C));
        memset(E,0,sizeof(E));
//        memset(arr,0,sizeof(arr));
        memset(presumE,0,sizeof(presumE));
        memset(suffixsumC,0,sizeof(suffixsumC));
        ans="";
        cin>>D>>S;
        for(int i=0;i<S;i++)
        {
            cin>>C[i]>>E[i];
            node tmp=node(C[i],E[i],i);
            arr[i]=tmp;

        }
        for(int i=0;i<D;i++)
        {
            cin>>A[i]>>B[i];
            ans+="x";

        }
        //small input
        if(S==1)
        {
            for(int i=0;i<D;i++)
            {
                if(trisearchsingle(i)==true)
                {
                    ans+="Y";
                }
                else
                {
                    ans+="N";
                }
            }
        }
        else if(S==2)
        {
            for(int i=0;i<D;i++)
            {
                if(trisearchouter(i)==true)
                {
                    ans+="Y";
                }
                else
                {
                    ans+="N";
                }
            }
        }
        else//large input
        {
            sort(arr,arr+S,cmp);
            for(int i=0;i<S;i++)
            {
                if(i==0)
                {
                    presumE[i]=arr[i].eating;
                }
                else
                {
                    presumE[i]=arr[i].eating+presumE[i-1];
                }

            }
            for(int i=S-1;i>=0;i--)
            {
                suffixsumC[i]=arr[i].coding+suffixsumC[i+1];
            }
            for(int d=0;d<D;d++)
            {
                int i=lower_bound(presumE,presumE+S,B[d])-presumE;
//                cout<<i<<endl;
                if(i==S)//no pre sum larger than B[d]
                {
                    ans[d]='N';
                    continue;
                }

                else if(presumE[i]>=B[d]&&suffixsumC[i+1]>=A[d])//no need to split time in one day
                {
//                        cout<<"set Y"<<endl;
                    ans[d]='Y';
                    continue;
                }
                else//split the time of current day
                {
//                        cout<<"here"<<endl;
                    bool res=split_single_day(i,d);
                    if(res==true)
                    {
                        ans[d]='Y';
                    }
                    else
                    {
                        ans[d]='N';
                    }
                    continue;
                }
            }
        }
//        sort(arr,arr+D,[](const node& x,const node& y)->bool{return x.index<y.index;});
        printf("Case #%d: ",ca);

        cout<<ans<<endl;

    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值