题解 P1570 【KC喝咖啡】

!80分歪解注意

我一看这题感觉就是搜索啊…前面大佬都是二分,说实话我没想到,题解也没人发搜索的(人家都二分过了谁管你搜索

后来证实了二分是正解,搜索是歪解…具体什么原因待会会讲

简单分析题意,我们可知对于每一种调料我们都可以选或者不选,选就获得一个收益且付出一个代价,不选就当无事发生

首先想到是背包?但是本题计算最终收益的方法是求和后再商…想了想感觉以我的水平很不可做orz

那就尝试一下搜索列举每种决策吧(我猜有人记不住数组含义,于是贴心的标出来hh

double tim[N],vl[N];//tim是花费时间,vl是价值(好吃程度
 void dfs(int k,double cst,double del,int c){
     //正在处理第k个,话费时间cst,收益好吃程度del,已经选了c中调料
     if(c==m){//选好了m种则更新答案
         ans=max(ans,del/cst);
         return;
     }
     if(k>n) return;//选出界了
 dfs(k+1,cst+tim[k],del+vl[k],c+1);//选择会带来收益和代价,以及选择数量++ dfs(k+1,cst,del,c);//不选则当无事发生,考虑下一个决策
 }

测了样例,感觉自己真的很不错,提交一看,80分

毕竟是纯暴力做法..

以下是我对于剪枝的一些思考和失败经历,由于太弱尝试几次并不成功,欢迎神牛指点一下orz

想了想,有一种剪枝策略是

用前缀和数组记下每个调料的收益,在对一个决策进行分析时,如果后面的调料全选也不如当前的状态优,则跳过这个决策

void dfs(int k,double cst,double del,int c){
     //正在处理第k个,话费时间cst,收益好吃del,已经选了c
     if(cst!=0 && (del+s[n]-s[k-1])/cst<=del/cst)return;
     if(c==m){
          //cout<<del<<':'<<cst<<endl;
          ans=max(ans,del/cst);
          return;
     }
     if(k>n)    return;
     dfs(k+1,cst+tim[k],del+vl[k],c+1);
     dfs(k+1,cst,del,c);
 }

测了样例,感觉自己真的很不错,交上去测,还是T

orz

其实仔细想想就能知道这种状态过于极端,虽然正确但并不能剪掉能影响算法时间复杂度的枝条,于是我不信邪,后面总不能收益都是1吧,我想牺牲一定的正确率来求能卡进1s时限(什么时候变成调参问题了)

if(cst!=0 && (del+s[n]-s[k-1])/cst<=del/(cst+10000))return;

测了样例,感觉自己真的很不错,交上去测,还是T

orz

其实仔细想想就能知道这种状态过于极端,虽然正确但并不能剪掉能影响算法时间复杂度的枝条,于是我不信邪,后面总不能收益都是1吧,我想牺牲一定的正确率来求能卡进1s时限(什么时候变成调参问题了)

if(cst!=0 && (del+s[n]-s[k-1])/cst<=del/(cst+10000))return;

剪掉了吗?剪掉了吗?并没有

下载分析数据范围后猛然发现,大数据点的话费都是1w+…那么可以知道这种剪枝策略不可能有效了

再分析一波,似乎可以这么剪一下:

对于一个状态,如果剩下的全选后面收益最大的调料且代价只加一个较小值(又调参草)还不如当前优,则可以不选

分析完发现,似乎可以用 $O(n^2)$ 的效率维护一个区间最值,您想用线段数+期望也行orz,实现这个剪枝,但是经过暴力分析大概可以估计,由于剪枝根本思想没变,效率也没有在时间复杂度上的提升,加之老代码跑完得快1min,于是果断放弃(然而得分的数据点都是2ms,3ms,可见数据水的可以hh

考场上的话,我就拿部分分滚粗了qwq

以上是我对于本题剪枝的一点点小思考和尝试,如果你有更好的剪枝策略请和我一起讨论,让本题有一个歪解AC吧(笑

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注