zoukankan      html  css  js  c++  java
  • 做算法第二章作业的编程题有感

    序言:第二章的题目实在是花费了我很多时间去想,甚至有些题目我并不能仅依靠自己的能力,还要上网去查明某些算法的实现,才能解出这道题。虽然本章作业的五道题目都不容易,但其思想没有脱离本章内容——分治法和递归,因此我们一一解读一下部分题目,并总结一下它们的解题思想。

    1.派

    题目内容:

    我的生日要到了!根据习俗,我需要将一些派分给大家。我有N个不同口味、不同大小的派。有F个朋友会来参加我的派对,每个人会拿到一块派(必须一个派的一块,不能由几个派的小块拼成;可以是一整个派)。

    我的朋友们都特别小气,如果有人拿到更大的一块,就会开始抱怨。因此所有人拿到的派是同样大小的(但不需要是同样形状的),虽然这样有些派会被浪费,但总比搞砸整个派对好。当然,我也要给自己留一块,而这一块也要和其他人的同样大小。

    请问我们每个人拿到的派最大是多少?每个派都是一个高为1,半径不等的圆柱体。

    输入格式:

    第一行包含两个正整数N和F,1 ≤ N, F ≤ 10 000,表示派的数量和朋友的数量。 第二行包含N个1到10000之间的整数,表示每个派的半径。

    输出格式:

    输出每个人能得到的最大的派的体积,精确到小数点后三位。

    输入样例:

    3 3
    4 3 3
    

    输出样例:

    在这里给出相应的输出。例如:

    25.133


    我的解题思路:
    (按例先放参考代码)
     1 #include <iostream>
     2 #include <cmath>
     3 using namespace std;
     4 const double PI = acos(-1.0);         //定义常量PI,这里用到了cmath头函数提供的求PI的反cos函数 
     5 
     6 bool divide(double array[], double currentDivide, int N, int people)
     7 {    //cuttentDivide为当前划分的量,大小为中间量(divideMid) 
     8     int ableNum = 0;    //ableNum表示按当前的分法,每个派能分几份 
     9     for (int i = 0; i < N; i++)
    10     {
    11         ableNum += int(array[i] / currentDivide);    //记录当前这个派能够分几份,然后再讲每个派的情况累加起来 
    12     }
    13     if (ableNum >= people) return true;        //如果分出来的份数大于或等于总人数,那么此情况可成立(但不是最佳分法) 
    14     else return false;                        //这种情况不可行,要缩小currentDivide以分更多的派给朋友和我 
    15 }
    16 
    17 int main() 
    18 {
    19     int N, F;     
    20     cin>>N>>F;
    21     int radius[10005];
    22     double v[10005];
    23     double vmax = 0;
    24     int people = F + 1;
    25     for (int i = 0; i < N; i++)        //输入每个派的半径,同时算出每个派的体积,并用vmax记录最大的体积 
    26     {
    27         cin>>radius[i];
    28         v[i] = radius[i] * radius[i] * PI;
    29         if (vmax < v[i])
    30         vmax = v[i];
    31     }
    32     double divideMin = 0, divideMax = vmax;        //定义划分的最小情况和最大情况 
    33     double divideMid;                            //定义划分中间量 
    34     while (divideMax - divideMin > 1e-7)        //循环终止条件:最大值小于最小值 
    35     {
    36         divideMid = (divideMin + divideMax) / 2;
    37         if (divide(v, divideMid, N, people))
    38         {
    39             divideMin = divideMid;    //如果当前分法成立,则尝试分更大的派给大家 
    40         }
    41         else 
    42         {
    43             divideMax = divideMid;    //如果当前分法根本无法满足我们的要求,那么我们分小一点保证能完成基本任务 
    44         }
    45     }
    46     double finalDivide;
    47     finalDivide = divideMid;
    48     printf("%.3lf", finalDivide);        //涉及到输出特定位小数,还是用printf安全! 
    49     return 0;
    50 }
     

      这一道题目非常有意思,所以先放到第一个讲。

      先解决输入问题:首先题目就有一点要我们注意:既然N是派的个数,F为来访朋友的个数,而“我”本人也是需要吃派的,所以实际分派人数为N+1。而题目要求的输出项为每人分到的派的体积,所以我在定义每个派的半径数组的同时,还算出了每一派的体积数组,还用一个vmax来记录最大的那个派的体积。

      再看一下题目要求的分派方法:所有人拿到的派是同样大小的(但不需要是同样形状的),虽然这样有些派会被浪费,但总比搞砸整个派对好。当然,我也要给自己留一块,而这一块也要和其他人的同样大小。

    这就意味着,每个人分到的派,都是从某一个给定的派中划分出来的(题目不允许拼派),所以会有以下情况:

    1.某一个派因为太小了,但它刚好是每个人能分到的大小,所以把它直接给了一个人。

    2.某一个派因为稍稍比算出来的每个人分到的派大,所以要浪费掉该派的剩下的部分。

    3.某一个派刚好是算出来的每个人分到的派的两倍体积!(只能刚好平分,不能一边大,一边小,不然违背题目要求,同时题目不允许拼派,所以也不能分一半大、一半小)

    根据二分法的思想,我定义了三个变量分别表示划分派的最小量、最大量、中间量,最小值初值为0,最大值初值为刚刚的得到的最大体积,这样的话,中间值就是最大派平分的情况,那么下面开始本题核心算法:判断到底该缩小中间量还是增大中间量的算法(函数名为divide的函数)

     1 bool divide(double array[], double currentDivide, int N, int people)
     2 {    //cuttentDivide为当前划分的量,大小为中间量(divideMid) 
     3     int ableNum = 0;    //ableNum表示按当前的分法,每个派能分几份 
     4     for (int i = 0; i < N; i++)
     5     {
     6         ableNum += int(array[i] / currentDivide);    //记录当前这个派能够分几份,然后再讲每个派的情况累加起来 
     7     }
     8     if (ableNum >= people) return true;        //如果分出来的份数大于或等于总人数,那么此情况可成立(但不是最佳分法) 
     9     else return false;                        //这种情况不可行,要缩小currentDivide以分更多的派给朋友和我 
    10 }

    为什么要用到判断是要缩小中间量还是增大中间量?

    因为中间量被传到divide函数中,变成了当前这种分派方法的划分派大小值(currentDivide)。

    如果,按照当前的划分大小来划分每一个派,结果所有派分好了,不够总人数大,不能保证朋友们和我都吃到派,这种方法太奢侈了!要求缩小每个人分到的派的大小,以保证我们的基本任务(每个人都吃到大小相等的派)!所以,继续尝试分下去!

    如果,按照当前的划分大小来划分每一个派,结果所有派分好了,够总人数,这样完成了我们的基本任务,但肯定会有浪费的派,为了响应国家节约粮食的号召,我们不能满足于大家吃很少很少的派,然后笑嘻嘻地丢掉了浪费掉的派!所以我们增大当前分法的划分大小,试一下,让大家都吃更多的派同时保证每个人吃到同样大小的派的基本任务!所以,继续尝试分下去!

    因为循环条件是最大值大于最小值,当我们不断修改最大值和最小值后,最后我们得到了最佳的划分大小并终止循环,就能得到最佳分法啦!

    不得不说用二分法解决这道题真的很有意思,我们既要保证每个人吃一样大的派,同时还要尽量减少浪费,因此要控制好循环的终止条件,并设置好每种情况要如何修改最大值和最小值(中间值取决于最大值和最小值)。

    2.二分法求函数的零点

    题目内容:

    有函数:f(x)=x​^5​​−15x^4​​+85x​^3​​−225x​^2​​+274^x−121 已知f(1.5)>0,f(2.4)<0 且方程f(x)=0 在区间[1.5,2.4] 有且只有一个根,请用二分法求出该根。

    我的解题思路:
    (按例先放参考代码)
     1 #include <iostream>
     2 #include <math.h>
     3 using namespace std;
     4 
     5 double expression(double x)        //用于表示题目的函数 
     6 {
     7     double fx;
     8     double x2 = pow(x, 2);
     9     double x3 = pow(x, 3);
    10     double x4 = pow(x, 4);
    11     double x5 = pow(x, 5);
    12     fx = x5 - 15 * x4 + 85 * x3 - 225 * x2 + 274 * x - 121;
    13     return fx;
    14 }
    15 
    16 int main()
    17 {
    18     //设定初始区间,并设置其的作用域为main函数内 
    19     double left = 1.5;
    20     double right = 2.4;
    21     while(left + 1e-7 < right)
    22     {
    23         double mid = (left + right) /2;    //定义mid存储中间值 
    24         if(expression(mid) > 1e-7)        //若mid大于0,则最小值变大 
    25             left = mid;
    26         else right = mid;                //若mid小于或等于0,则最大值缩小 
    27     }
    28     if(fabs(expression(left)) < 1e-7)
    29         cout<<left;
    30     return 0;
    31 }

    这道题的要求简单粗暴,就是要让你解出能让函数为0的x值,同时题目给定了一个定义域,我们只要针对这个定义域使用二分法来不断缩短其范围即可。

    那么,我们在定义好定义域的最大、最小值后,开始判断如何更改中间值:

    1.如果中间值对应的fx大于0,那么证明中间值还有潜力变大,所以将最小值变大(将其等于当前的中间值)。

    2.如果中间值对应的fx小于0,那么证明中间值要缩小了,否则得不到能让fx为0的x值,所以将最大值变小(将其等于当前的中间值)。

    通过不断循环,我们不断压缩题目给定的定义域范围,最后我们能够得出一个确切的值使得fx为0啦。

    这道题的要求简单,其思想也很简单——用二分法压缩定义域范围求出题目要求的值。虽然看起来很简单,但我一开始做题的时候用了递归,又因为没设置好结束条件,导致程序陷入死循环。书上提供的二分法,是用递归思想没错,但不代表二分法就要用递归,这两道题的while就是最好的证明,因此二分法的思想要掌握好:分情况来改变中间值,设置好循环终结条件后,得出的结果就是我们想要的结果啦。

     

     

  • 相关阅读:
    Skimage=scikit-image SciKit 包的模块(转载)
    python day12
    python day11
    python day10
    python day9
    python day8
    python day7
    python day6
    python 第五天
    python 第四天
  • 原文地址:https://www.cnblogs.com/besthunterhj/p/11487552.html
Copyright © 2011-2022 走看看