洛谷二分答案问题

二分答案关键有2点

1. 怎么写judge函数,其实就是根据题意想办法判断我们枚举的这个答案是否可行(合法)。

2.找到了一个可行解(合法的,超过题目限制是不合法),再往左边还是右边查找看是否有更优的解是个问题,需要好好想想。

(这两点想明白了,二分就会异常简单甚至比暴力枚举还要简单。因为暴力枚举你还要考虑=0不移动时的特殊情况,而二分枚举就不用考虑特殊边界等特殊情况,!所以推荐直接二分查找枚举答案)

 

 

P2678跳石头:https://www.luogu.org/problemnew/show/P2678#sub

 

题意方面:最开始就是不知道它最小最大是什么意思。。。

题目讲的很绕,很烦,理清了后本质就是:求的是去掉几个数,所有得可能的排列情况中最小距离,再取这些最小距离中最大的那一个就是答案(最小值最大化)

 

一阶段:想的是搜索,找出所有剩N-M个数的排列(升序不重复),求出最小值,再在这些最小值中取最大的。(这是最好想的也最暴力的方法,但严重超时!)

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 const int inf=0x7fffffff;
 6 int n,B,a[100001];
 7 int used[100001];
 8 int Min=1e9,Max=0;
 9 void so(int last,int step)
10 {
11     if(step==B+1)
12     {
13         Min=1e9;
14         for(int i=1;i<=B-1;i++)
15         {
16             Min=min(Min,used[i+1]-used[i]);
17         }
18         Max=max(Max,Min);
19         return;
20     }
21     
22     int K=B-step;
23     for(int i=last;i<=n;i++)
24     {
25         //if(i+K>n) break;
26         used[step]=a[i];
27         so(i+1,step+1);
28     }
29 }
30 
31 int main(){
32     scanf("%d%d",&n,&B);
33     for(int i=1;i<=n;++i)scanf("%d",&a[i]);
34     sort(a+1,a+n+1);
35     
36     so(1,1);
37     
38     printf("%d\n",Max);
39     
40     return 0;
41 }

 

二阶段:看了某位大佬的一句话,“正向求出答案不好入手,求解答案远远没有验证答案简单“!(重大启发啊)

而且题目本来就是要求所有情况中最大的,本来就是暴力,就看你怎么优化暴力了,只是现在仅仅想到验证答案。就从头到尾一个一个验证答案是否符合,<=M次都是符合的选出一个最大的就行,只要>M后面就肯定更大,所以可直接结束!可以发现这就是线性查找(效率很低,所以有了后面的二分查找优化!但理解暴力算法也很重要),2000ms+过了。。

注意:暴力循环时<M(错误)和<=M(正确)的问题区别!虽然题目是单调递增性质,=N也一定比<M距离大,但有可能有特殊数据啊,有这样一句话“可能最后答案偏偏没有用完M呢”!恍然大悟!

1.像0,6这样移动0次肯定要比移动1次更大啊(你都没法取到1次,所以最终还是0);同理0,5,6移动1次肯定比移动2次甚至更多要优啊!(你都没法取到2次,最大到L取1次而已,没用完M)

2.就像上面情况,出题人专门给你个偏大的M,答案就是不用完M时才能取到,这时你只判断==M时不就错了吗!

 3.也就是说,题目一定存在这样的点,<M一定比=M解更优,也一定存在这样的情况!(不信可参考进击的奶牛5,6点!只要<M过=M就不用要。)

4.关键特殊1

最终终于找到最后的bug了!很简单嘛,他如果给你一个故意偏大的M你肯定取不完,所以答案肯定就是<M是更优啊!而你的程序只判断==M就是0输出L也大多正确。如下特殊数据:!

1 2 3 4 5 6

取走4个点是最优的!如果给你M=5或6或更多!你就取不到!(因为循环答案最多从1到a[n]-a[1]即5!所以最多取到4)

5.关键特殊2

1 2 3 4

1,0次(发现并没有M=1次时的情况!关键,你只找M==1的答案,结果没有,所以就0输出L错,实际答案应该是1,移动1次最近距离还是1!)

2,2次

3,2次

总之,关键,你只直接判断M次是有问题的,因为这个M次答案可能没有,要么比M多,要么比M少,跳跃性的,像1次,这时候就只能往前走了!退一步答案小一点即<M也勉强算最优解!(因为你M多了不够,只能舍弃往前走了!)

(所以移动次数多距离大递增性没错,但没有你移动这个次数的答案你不得只能舍弃往前走了!!(<M成立在这!舍弃没用部分往前取小的最优),而不能往后走因为你次数不够!)

 

所以啊,你想不到的东西不代表它不存在,只是你没想到,想出来时是如此激动人心!(就是太花时间了,想了整整两天~~)

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 using namespace std;
 5 const int maxn=1e6+5;
 6 int a[maxn];
 7 int L,N,M;
 8 
 9 int judge(int x)
10 {
11     int last=0,cnt=0;
12     for(int i=1;i<=N+1;i++)
13     {
14         if(a[i]-a[last]<x) cnt++;
15         else last=i;
16     }
17 
18     return cnt;
19 }
20 
21 int main()
22 {
23     ios::sync_with_stdio(false); cin.tie(0);
24 
25    cin>>L>>N>>M;
26    for(int i=1;i<=N;i++) cin>>a[i];
27 
28    sort(a+1,a+1+N);
29    a[N+1]=L;
30    int ans=0;
31    for(int i=1;i<=L;i++)
32    {
33        int t=judge(i);
34        if(t<=M)//必须是<=M次,不要认为只有=M次时才最远(虽然是线性增长的,但有特殊数据,像只有起终2数0次移动便是最好的)(也就是<M次也可以)
35        {
36            ans=max(ans,i);
37        }
38        else break;//后面更大
39    }
40 
41    cout<<ans<<endl;
42 
43    return 0;
44 
45 }

 

三阶段:便是对暴力线性验证的优化,二分查找答案验证。70ms过!

 1 #include <iostream>
 2 #include <string>
 3 #include <algorithm>
 4 #include <cstdio>
 5 #include <cstring>
 6 using namespace std;
 7 typedef long long ll;
 8 const int maxn=1e6+5;
 9 int a[maxn];
10 int L,N,M;
11 
12 int judge(int x)
13 {
14     int last=0,cnt=0;
15     for(int i=1;i<=N+1;i++)
16     {
17         if(a[i]-a[last]<x) cnt++;
18         else last=i;
19     }
20 
21     return cnt;
22 }
23 
24 int main()
25 {
26     ios::sync_with_stdio(false); cin.tie(0);
27 
28     cin>>L>>N>>M;
29     for(int i=1;i<=N;i++) cin>>a[i];
30 
31     sort(a+1,a+1+N);
32     a[N+1]=L;
33     int l=1,r=L,ans=0;
34     while(l<=r)
35     {
36         int mid=(l+r)/2;
37         int t=judge(mid);
38 
39         if(t>M) r=mid-1;//太大了,后面更大
40         else { ans=mid; l=mid+1; }//t<=M次就是可行解,扩大范围试试还有没有更大解
41     }
42 
43     cout<<ans<<endl;
44 
45     return 0;
46 }

 

完。

posted @ 2018-10-26 15:48  RedBlack  阅读(896)  评论(0编辑  收藏  举报