• 算法答疑---10:河中跳房子


    算法答疑---10:河中跳房子

    一、总结

    一句话总结:一串数,去掉其中几个,使得剩余数之间的最短距离最长。

    a、二分法,遍历判断每一个值(从起点到终点)

    b、遍历每个值的过程中,判断每次剩余的数目是不是符合题目要求

    c、找到解之后要继续找,因为要找最优解

    1、为什么直接输出mid会错(两个代码唯一的区别就是用ans = mid记录了结果,另一个是直接用mid输出了结果)?

    直接输出mid肯定不对啊
    因为如果用ans记录mid的话,那个mid肯定是我们算好的答案
    而直接输出mid的话,可能是正确答案后的一次,或者n的mid值
    当然也可能是正确答案,像这个题

    比如,当mid为5已经满足了,但是程序还会在6-11中继续寻找答案,所以这种情况下,输出ans是对的,输出mid是错的


    这样表述很抽象,不知道你懂了没有

    就是如果用ans记录的方式,输出的一定是正确的mid,而直接输出mid,可能因为接下来的计算让mid改变从而不是正确答案了

    2、如何快速高效的看懂一段代码(也算看懂题目的思路)?

    打印中间结果,就知道题目每一步在干什么了

    就能知道变量表达式是什么意思了

     1 #include<iostream>
     2 #define MAXN 50010
     3 using namespace std;
     4 int a[MAXN];
     5 int l, n, m;
     6 
     7 bool find(int mid)
     8 {
     9     cout<<"mid: "<<mid<<endl;
    10     int now = 0, num = 0;
    11     for(int i = 1; i <= n + 1; ++ i)
    12     {
    13         if(a[i] - a[now] >= mid)
    14         {
    15             cout<<a[i]<<" "<<a[now]<<endl;
    16             ++ num;//num表示剩下的石头(除第一块) 
    17             now = i;
    18         }        
    19     }
    20     cout<<num<<" "<< n - m + 1<<"    end"<<endl;
    21     if(num >= n - m + 1)//num >= n - m + 1表示剩下的石头比较多,说明石头去少了,说明最短距离还不是最长,所以往二分的右边走 
    22         return 1;
    23     else
    24         return 0;
    25 } 
    26 
    27 int main()
    28 {
    29     freopen("test.txt","r",stdin); 
    30     cin >> l >> n >> m;
    31     for(int i = 1; i <= n; ++i)
    32         cin >> a[i];
    33     int le = 0, mid, ans;
    34     a[n+1] = l;
    35     int ri = a[n+1];
    36     //测试 
    37     for(int i=0;i<=n+1;i++) cout<<a[i]<<" ";cout<<endl;
    38     while(le <= ri)
    39     {
    40         cout<<"left-right: "<<le<<" "<<ri<<endl;
    41         mid = (le + ri)/2;//mid为最长可能的最短跳跃距离 
    42         
    43         //cout<<mid<<" "<<find(mid)<<"----------"<<endl;
    44         if(find(mid))
    45         {
    46             ans = mid;
    47             le = mid + 1;
    48         }
    49         else
    50             ri = mid - 1;
    51             
    52     }
    53     cout << ans << endl;
    54     return 0;     
    55 }

    二、河中跳房子-题目描述

    总时间限制: 
    1000ms
     
    内存限制: 
    65536kB
    描述

    每年奶牛们都要举办各种特殊版本的跳房子比赛,包括在河里从一个岩石跳到另一个岩石。这项激动人心的活动在一条长长的笔直河道中进行,在起点和离起点L远 (1 ≤ L≤ 1,000,000,000) 的终点处均有一个岩石。在起点和终点之间,有N (0 ≤ N ≤ 50,000) 个岩石,每个岩石与起点的距离分别为Di (0 < Di < L)。

    在比赛过程中,奶牛轮流从起点出发,尝试到达终点,每一步只能从一个岩石跳到另一个岩石。当然,实力不济的奶牛是没有办法完成目标的。

    农夫约翰为他的奶牛们感到自豪并且年年都观看了这项比赛。但随着时间的推移,看着其他农夫的胆小奶牛们在相距很近的岩石之间缓慢前行,他感到非常厌烦。他计划移走一些岩石,使得从起点到终点的过程中,最短的跳跃距离最长。他可以移走除起点和终点外的至多(0 ≤ M ≤ N) 个岩石。

    请帮助约翰确定移走这些岩石后,最长可能的最短跳跃距离是多少?

    输入
    第一行包含三个整数L, N, M,相邻两个整数之间用单个空格隔开。
    接下来N行,每行一个整数,表示每个岩石与起点的距离。岩石按与起点距离从近到远给出,且不会有两个岩石出现在同一个位置。
    输出
    一个整数,最长可能的最短跳跃距离。
    样例输入
    25 5 2
    2
    11
    14
    17
    21
    样例输出
    4
    提示
    在移除位于2和14的两个岩石之后,最短跳跃距离为4(从17到21或从21到25)。

    三、解析及代码

    首先,输入一个数组,然后用二分查找。先把数组分成两半,在左边一半从第一个开始比对。如果第一个比最小距离大,移除第一个,再看第二个。。。。。。第三个。。一直到L/2个。如果移除的比预定的多,说明要缩小范围,从开头到L/4里面找;如果比预定的少,扩大范围,从MID到L/2右边半个找;如果还是大,缩小范围;如果还是小,扩大范围,一直到应该减少的和实际减少的一致,输出。

    样例过程:

    测试代码:

     1 #include<iostream>
     2 #define MAXN 50010
     3 using namespace std;
     4 int a[MAXN];
     5 int l, n, m;
     6 
     7 bool find(int mid)
     8 {
     9     cout<<"mid: "<<mid<<endl;
    10     int now = 0, num = 0;
    11     for(int i = 1; i <= n + 1; ++ i)
    12     {
    13         if(a[i] - a[now] >= mid)
    14         {
    15             cout<<a[i]<<" "<<a[now]<<endl;
    16             ++ num;//num表示剩下的石头(除第一块) 
    17             now = i;
    18         }        
    19     }
    20     cout<<num<<" "<< n - m + 1<<"    end"<<endl;
    21     if(num >= n - m + 1)//num >= n - m + 1表示剩下的石头比较多,说明石头去少了,说明最短距离还不是最长,所以往二分的右边走 
    22         return 1;
    23     else
    24         return 0;
    25 } 
    26 
    27 int main()
    28 {
    29     freopen("test.txt","r",stdin); 
    30     cin >> l >> n >> m;
    31     for(int i = 1; i <= n; ++i)
    32         cin >> a[i];
    33     int le = 0, mid, ans;
    34     a[n+1] = l;
    35     int ri = a[n+1];
    36     //测试 
    37     for(int i=0;i<=n+1;i++) cout<<a[i]<<" ";cout<<endl;
    38     while(le <= ri)
    39     {
    40         cout<<"left-right: "<<le<<" "<<ri<<endl;
    41         mid = (le + ri)/2;//mid为最长可能的最短跳跃距离 
    42         
    43         //cout<<mid<<" "<<find(mid)<<"----------"<<endl;
    44         if(find(mid))
    45         {
    46             ans = mid;
    47             le = mid + 1;
    48         }
    49         else
    50             ri = mid - 1;
    51             
    52     }
    53     cout << ans << endl;
    54     return 0;     
    55 }

    AC代码:

     1 #include<iostream>
     2 #define MAXN 50010
     3 using namespace std;
     4 int a[MAXN];
     5 int l, n, m;
     6 
     7 bool find(int mid)
     8 {
     9     int now = 0, num = 0;
    10     for(int i = 1; i <= n + 1; ++ i)
    11     {
    12         if(a[i] - a[now] >= mid)
    13         {
    14             ++ num;
    15             now = i;
    16         }        
    17     }
    18     if(num >= n - m + 1)
    19         return 1;
    20     else
    21         return 0;
    22 } 
    23 
    24 int main()
    25 {
    26     cin >> l >> n >> m;
    27     for(int i = 1; i <= n; ++i)
    28         cin >> a[i];
    29     int le = 0, mid, ans;
    30     a[n+1] = l;
    31     int ri = a[n+1];
    32     while(le <= ri)
    33     {
    34         mid = (le + ri)/2;//mid为最长可能的最短跳跃距离 
    35         if(find(mid))
    36         {
    37             ans = mid;
    38             le = mid + 1;
    39         }
    40         else
    41             ri = mid - 1;
    42             
    43     }
    44     cout << ans << endl;
    45     return 0;     
    46 }
     1 #include <iostream>
     2 #include <algorithm>
     3 #include <cstdio>
     4 using namespace std;
     5 int l, n, m, a[50003], ans;                     /* 定义变量 */
     6 int count( int mid )
     7 {
     8     int j = 0, x = 0;                       /* x记录删去石头数 */
     9     for ( int i = 1; i <= n + 1; i++ )
    10     {
    11         if ( a[i] - a[j] < mid )        /* 石头间距离如果小于枚举的,就记录并删去。 */
    12             x++;
    13         else j = i;                     /* 否则移动起点。 */
    14     }
    15     return(x);
    16 }
    17 
    18 
    19 int main()
    20 {
    21     scanf( "%d%d%d", &l, &n, &m );
    22     for ( int i = 1; i <= n; i++ )
    23         scanf( "%d", &a[i] );
    24     sort( a + 1, a + 1 + n );               /* 别忘了排序。 */
    25     a[n + 1] = l;                           /* 记录终点 */
    26     int left = 1, right = l;
    27     while ( left <= right )                 /* 开始二分。 */
    28     {
    29         int middle = (left + right) / 2;
    30         if ( count( middle ) <= m )     /* 计数函数 */
    31         {
    32             ans    = middle;
    33             left    = middle + 1;
    34         }else right = middle - 1;
    35     }
    36     printf( "%d", ans );                    /* 输出 */
    37     return(0);
    38 }
     
  • 相关阅读:
    转发与重定向的区别
    Servlet开发详讲
    Servlet的常见错误
    HTTP请求方式之POST和GET的区别
    Spring各种类型数据的注入
    Spring容器的基本使用
    Python接口自动化-测试用例编写
    Python接口自动化-设计测试用例
    python简明教程之数据结构(列表、元组、字典、集合)
    python简明教程之函数
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/9575354.html
Copyright © 2020-2023  润新知