7 条题解

  • 12
    @ 2022-4-29 13:22:14

    二分答案题目的特性:

    1. 答案一般具有单调性

    2. 有如下关键字:最短的最大,最小的最大,最大的最小等。

    本题二分答案的主要思路就是对跳跃距离进行二分,

    跳跃距离最小是1,最大是x。

    因此对该区间二分,当我们选择mid这个距离进行跳跃,

    如何设置检查函数?

    枚举验证,看在当前这个距离下,是否可以移走不大于z的石头个数

    如果可以,就说明这个距离没问题,否则不行。

    需要注意题目说的是起点和终点之间有n个石头,

    因此我们还需要人为加上a[n+1]=xa[n+1]=x //x是终点到起点的距离

    int x,y,z,l,r,mid,ans,a[50005];
    //x是起点到终点距离,y是岩石个数,z是最多移走的个数。
    bool check(int x)
    {
    	int sum=0,now=0;
    	for(int i=1;i<=y;i++)
    	{
    		if(a[i]-a[now]<x) sum++;
    		else now=i;
    	}
    	if(sum<=z) return true;
    	else return false;
    }
    

    核心代码如上,其余就是二分答案的模板代码。

    注意记得排序石块的距离。

    并设置a[n+1]=xa[n+1]=x

    然后 l=1,r=xl=1,r=x。二分即可

    • 6
      @ 2023-7-22 20:23:53

      首先,这是一道二分题,初始化左端点0,右端点L;

      #include <cstdio>
      using namespace std;
      int len,m,n,l,r,mid,ans,d[50005];
      bool check(int x){
          int lastpos=0,cnt=0;
          for(int i=1;i<=n;i++)d[i]-d[lastpos]<x?cnt++:lastpos=i;
          return d[n+1]-d[lastpos]>=x && cnt<=m;//因为最后一块不能移走,所以要单独考虑
      }
      int main(){
          scanf("%d%d%d",&len,&n,&m);
          for(int i=1;i<=n;i++)scanf("%d",&d[i]);
          d[n+1]=len,d[0]=0,l=1,r=len;
          while(l<=r){
              mid=(l+r)/2;
              check(mid)?ans=mid,l=mid+1:r=mid-1;
          }
          printf("%d\n",ans);
          return 0;
      }
      
      • 4
        @ 2023-6-26 21:53:48
        #include <iostream>
        using namespace std;
        int n, m, k, r, mid, ans;
        int a[500000000];
        long long l;
        bool check(int x)
        {
            int last = 0;
            int sum = 0;
            for (int i = 1; i <= n; i++)
            {
                if (a[i] - last < x)
                {
                    sum++;
                }
                else
                {
                    last = a[i];
                }
                if (sum > k)
                    return false;
            }
            return true;
        }
        int main()
        {
            cin >> m >> n >> k;
            for (int i = 1; i <= n; i++)
                cin >> a[i];
            a[n + 1] = m;
            l = 1;
                r = m;
                while (l <= r)
                {
                    mid = (l + r) / 2;
                    if (check(mid))
                    {
                        ans=mid;
                        l=mid+1;
                    }
                    else
                    {
                        r=mid-1;
                    }
                }
                cout << ans << endl;
            return 0;
        }
        
        • 1
          @ 2023-6-16 17:45:10

          二分答案,左端点是1,右端点是l,运用check函数判断是否能成立。

          #include <iostream>
          using namespace std;
          int l,n,m,d[10000000];
          bool check(int mid){
              int sum=0,next=0;
              for(int i=1;i<=n;i++)
                  if(d[i]-next<mid)
                      sum++;
                  else
                      next=d[i];
              return sum<=m;
          }
          int main(void){
              cin>>l>>n>>m;
              for(int i=1;i<=n;i++)
                  cin>>d[i];
              int L=1,R=l,mid,ans;
              while(L<=R){
                  mid=(L+R)/2;
                  if(check(mid)){
                      L=mid+1;
                      ans=mid;
                  }
                  else
                      R=mid-1;
              }
              cout<<ans;
              return 0;
          }
          
          • 0
            @ 2023-6-10 14:26:29
            #include <iostream>
            using namespace std;
            int n, m, k, l, r, mid, ans;
            int a[50005];
            bool check(int x)
            {
            	int last = 0;
            	int sum = 0;
            	for (int i = 1; i <= n; i++)
            	{
            		if (a[i] - last < x)
            		{
            			sum++;
            		}
            		else
            		{
            			last = a[i];
            		}
            		if (sum > k)
            			return false;
            	}
            	return true;
            }
            int main()
            {
            	cin >> m >> n >> k;
            	for (int i = 1; i <= n; i++)
            		cin >> a[i];
            	a[n + 1] = m;
            	l = 1;
            	r = m;
            	while (l <= r)
            	{
            		mid = (l + r) / 2;
            		if (check(mid))
            		{
            			ans = mid;
            			l = mid + 1;
            		}
            		else
            		{
            			r = mid - 1;
            		}
            	}
            	cout << ans << endl;
            	return 0;
            }
            
            • -3
              @ 2024-5-12 17:55:45

              hetao28428387 LV 9

              P1409[普及~提高][NOIP2015提高组]跳石头

              说明

              一年一度的"跳石头"比赛又要开始了!

              这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有N块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

              为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走M块岩石(不能移走起点和终点的岩石)。

              输入格式

              第一行包含三个整数L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。保证L≥1且N≥M≥0。

              接下来 N 行,每行一个整数,第i行的整数Di(0<Di<L),表示第i块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

              输出格式

              一个整数,即最短跳跃距离的最大值。

              样例

              输入数据1

              25 5 2
              2
              11
              14
              17
              21
              

              输出数据1

              4
              

              提示

              输入输出样例1说明:将与起点距离为2和14的两个岩石移走后,最短的跳跃距离为4(从与起点距离17的岩石跳到距离21的岩石,或者从距离21的岩石跳到终点)。

              另:对于20%的数据,0≤M≤N≤10。

              对于50%的数据,0≤M≤N≤100。

              对于100%的数据,0≤M≤N≤50,000,1≤L≤1,000,000,000 。

              代码:

              这是一道二分题,初始化左端点0,右端点L。

              #include<cstdio>
              int b,c,d,e,f,g,h,a[50000];
              bool check(int i){int j=0,k=0;for(int l=1;l<=d;l++){a[l]-a[j]<i?k++:j=l;}return a[d+1]-a[j]>=i&&k<=c;}
              int main(){
              scanf("%d%d%d",&b,&d,&c);
              for(int l=1;l<=d;l++)scanf("%d",&a[l]);
              a[d+1]=b,a[0]=0,e=1,f=b;
              while(e<=f){g=(e+f)/2;check(g)?h=g,e=g+1:f=g-1;}
              printf("%d\n",h);
              return 0;
              }
              

              代码已消毒,请放心食用。

              • -3
                @ 2023-4-1 16:30:08
                #include <iostream>
                #include <list>
                #define maxn 50005
                const int inf = 0x7fffffff;
                using namespace std;
                int l, n, m;
                int dist[maxn];
                bool check(int k);
                int main()
                {
                    cin >> l >> n >> m;
                    for (int i = 1; i <= n; i++)
                        cin >> dist[i];
                    dist[++n] = l;
                
                    int left = 0, right = 1000000005, mid;
                    while (left < right)
                    {
                        mid = (left + right + 1) / 2;
                        if (check(mid)) // 能否仅通过移去m块石头使所有跳跃距离≥mid
                            left = mid;
                        else
                            right = mid - 1;
                    }
                    cout << left << endl;
                    return 0;
                }
                bool check(int k)
                {
                    int cnt = 0;
                    int last = 0;
                    for (int i = 1; i <= n; i++)
                    {
                        if (dist[i] - dist[last] < k)
                            cnt++;
                        else
                            last = i;
                        if (cnt > m)
                            return false;
                    }
                    return true;
                }
                
                • 1

                [普及~提高][NOIP2015 提高组] 跳石头

                信息

                ID
                1409
                时间
                1000ms
                内存
                256MiB
                难度
                6
                标签
                递交数
                1065
                已通过
                365
                上传者