• 二分、三分、01分数规划【第III弹】


    目录

    一、三分

    例题:

    1、最长影子

    2、最小圆盘覆盖(三分套三分)

    3.传送带

    二、01分数规划

    理解

     例题:小咪买东西


    一、三分

     对于单峰单谷函数求最值,在left~right区间内,取两个点(图中mid 和 midmid),然后对于“谷”,函数值 f[mid] > f[midmid] 那么最大值一定在区间mid~midmid 或 left~mid 之间,就可以舍弃区间mid~right了,再以midmid为右端点依次“三分”…… 单谷函数类似。

    【当然我们也可以对它的导函数二分处理】(可导)

    例题:

    1、最长影子

     (用相似三角形)

    2、最小圆盘覆盖(三分套三分)

    一个平面上有一些点,要求用一个圆盘能覆盖所有点(可以在边缘上可以在圆内),求满足要求的圆的最小半径。

    【这个题在某场比赛中有个升级版:圆盘->球,二维->三维】其实这种题是有专门的计算几何算法的(貌似叫“模拟退火算法”?),复杂度更低,但是这里用三分也“无伤大雅”。。

    思路:圆心坐标y轴确定时,对x,从一端扫到另一端(左->右/ 右->左),半径是先减后增的,同样的道理,x轴确定时,扫y坐标也是先减后增的。那么问题来了,两次的“最低点” 相同吗?即它们整体上是单点吗?答案:是的。【雨jj 的解释是不管是x轴还是y轴,你在平面内随意画两条直线,它都是符合这个性质的,那么那个最低点就是重合的】

    所以就可以 “三分套三分地求x坐标和y坐标”了,同理,球的话,就三个三分套起来。。。

    “ 三分的题考得不是特别多,如果出的话,很有可能就是这种题 ” 

    3.传送带

    [SCOI2010]传送带

     来了来了 ,一个 “三分套三分” 的题~

    首先题目满足“单谷”(只不过是“一个套一个”),在线段AB上取一点m, CD上取一 n ,设路线A-m-n-D为用时最短。第一个三分(写在主函数里)调用check1(), 三分选m点,check1() 里调用check2()【“套”第二个 三分】选 n点.

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. const double eps=1e-4;//减小误差
    6. struct nt{
    7. double x,y;
    8. }a,b,c,d,m,n;
    9. double p,q,r;
    10. double dis(nt a,nt b){
    11. return sqrt(eps+(a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); //加一个极小值eps减小误差
    12. }
    13. double check2(double x){ //x为C-n长度
    14. double tp=x/dis(c,d); //根据 比例 算坐标
    15. n = {c.x+tp*(d.x-c.x), c.y+tp*(d.y-c.y)};
    16. return dis(n,m)/r + (dis(c,d)-x)/q; //前面一个三分已经算出了m点
    17. } //check2()返回m-n段、n-D段 花费时间
    18. double check1(double x){ //x为A-m长度
    19. double tp=x/dis(a,b);
    20. m = {a.x+tp*(b.x-a.x), a.y+tp*(b.y-a.y)};
    21. double l=0, r=dis(c,d), lm, rm;
    22. for(int i=0;i<1000;i++){
    23. lm=l+(r-l)/3, rm=r-(r-l)/3; //三分的 左“中点” 右“中点”
    24. if(check2(lm) >= check2(rm)) l=lm;
    25. else r=rm;
    26. }
    27. return x/p+check2(l); //A-m段 + check2()返回时间 ==总时间
    28. }
    29. int main(){
    30. scanf("%lf %lf %lf %lf %lf %lf %lf %lf",&a.x,&a.y,&b.x,&b.y,&c.x,&c.y,&d.x,&d.y);
    31. scanf("%lf %lf %lf",&p,&q,&r);
    32. double l=0, r=dis(a,b), lm, rm;
    33. for(int i=0;i<1000;i++){
    34. lm=l+(r-l)/3, rm=r-(r-l)/3;
    35. if(check1(lm) >= check1(rm)) l=lm;
    36. else r=rm;
    37. }
    38. printf("%.2f",check1(l));
    39. return 0;
    40. }

    注意:关于eps的减小误差的原因看这篇:浮点数数据误差eps处理(详细解析)_扣子不会飞的博客-CSDN博客_eps 误差

    【不加eps过不了qwq】

    plus: “神兽” 代码:(皮一下很开心 ^ ^)【就是看上面那题的题解时看到的!】

    1. /**
    2. *  ┏┓   ┏┓+ +
    3. * ┏┛┻━━━┛┻┓ + +
    4. * ┃       ┃
    5. * ┃   ━   ┃ ++ + + +
    6. * ████━████+
    7. * ◥██◤ ◥██◤ +
    8. * ┃   ┻   ┃
    9. * ┃       ┃ + +
    10. * ┗━┓   ┏━┛
    11. *   ┃   ┃ + + + +Code is far away from  
    12. *   ┃   ┃ + bug with the animal protecting
    13. *   ┃    ┗━━━┓ 神兽保佑,代码无bug 
    14. *   ┃        ┣┓
    15. *   ┃        ┏┛
    16. *  ┗┓┓┏━┳┓┏┛ + + + +
    17. *    ┃┫┫ ┃┫┫
    18. *    ┗┻┛ ┗┻┛+ + + +
    19. */

    二、01分数规划

    理解

     思考

    怎么选呢?是选前k个a[i]/b[i]最大的吗?很好!->给反例:200/99 , 403/201 , 2/1 三个数中选两个,按此方法则是(200+403)/(99+201) == 603/300  但是如果选 第一个和第三个数:(200+2) /  (99+1) == 202/100 > 603/300 所以,这是不对的,因为求的是整体的除的结果,虽然403/201 比2/1大,但是它的分母对第一个数的分母来说很大......

    我们再来谈谈为什么这个叫“01分数规划”吧~ “01”-> 选还是不选 ,"分数" ->分式形式,“规划”->求最值或是啥的(有规划才能最好嘛是吧...[是不是有点勉强]

    做法:公式变形-> 分式变成移到一边的式子,转化成二分。

     例题:小咪买东西

    传送门

     

    1. #include
    2. #include
    3. using namespace std;
    4. typedef long long ll;
    5. const int M=1e4+6;
    6. ll n, k, c[M], v[M],a[M];
    7. bool check(ll x){
    8. ll sum=0;
    9. for(int i=1;i<=n;i++)a[i]=v[i]-x*c[i];
    10. sort(a+1,a+1+n);
    11. for(int i=n;i>n-k;i--)sum+=a[i];
    12. return sum>=0;
    13. }
    14. void solve(){
    15. cin>>n>>k;
    16. for(int i=1;i<=n;i++)cin>>c[i]>>v[i];
    17. ll l=0,r=1e8,mid;
    18. while(l
    19. mid=l+r+1>>1;
    20. if(check(mid))l=mid;
    21. else r=mid-1;
    22. }
    23. cout<'\n';
    24. }
    25. int main(){
    26. ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    27. int t;cin>>t;
    28. while(t--)solve();
    29. return 0;
    30. }

  • 相关阅读:
    学习阶段单片机买esp32还是stm32?
    微软Copilot+ PC:Phi-Silica
    Windows下DOS窗口修改编码
    项目管理具体是做什么的?
    BUG:AttributeError: module ‘numpy‘ has no attribute ‘bool‘.
    宣布 .NET MAUI 支持 .NET 7 Release Candidate 2
    Docker第四天作业
    Go语言操作grpc详细使用
    ffmpeg 开发笔记
    电感啸叫产生的根本原因及解决方法
  • 原文地址:https://blog.csdn.net/m0_60523890/article/details/125993717