51nod 1105 二分答案法标准题目

二分答案法例题,用于练习二分答案的基本思想非常合适,包括了思维方式转换的内容(以前我们所做的一直是利用二分法求得数组元素对应指针之类,但是现在是直接对答案进行枚举)。

思路是:首先对输入数组进行排序,使得a,b两数组都相对有序。

接下来对每个数字进行二分枚举,之后,通过判断这个数组当前的排名来进行下一步的二分。

对于每个数字排名的方式通过对枚举每一个a数组中的位置来确定,之后二分对应的数组元素的位置得到,时间复杂度是NLOGN;

整个算法时间复杂度是NLOGNLOG(N*N)带入50000计算得到的值大概是两千六百万的样子,实际加上cin.sync_with_stdio(false);跑出来的结果大概是300毫秒的样子。。看上去51nod的服务器还是比较快的。事实上,因为输入数据量大,使用输入优化一定程度上有些奇妙的速度优势。但是其实不加这个优化大概时间在500毫秒,其实也是差不多的。

 说实话当时再设计完这个代码的时候超级心虚。。。觉得随便那个常数没搞好就得玩炸了。。。。结果来看危险也许是挺危险,但是也还有一定的容错空间。

 

最初的再设计出来统计大于某元素数量的方式之后发现复杂度有点小爆炸。。。以至于对于进一步的爆炸设计有些心虚。。。。

当时提出的替代方案是首先从A中枚举在一个数字,让他满足A[K]*B[0]<TARGET&&A[K]*B[N-1]>TARGET

之后进行下一部枚举——找到一个B元素,使得A[k]*B[I]<TARGET&&A[K]*B[I+1]>TARGET

之后使用B[I]反推A[I]。但是发现只这种方法其实要正确实现复杂度反而比之前的方案复杂度更高,因为在反推这一步没法使用二分法——无法凭空确定是哪个A[k]正好能满足要求,也同时无法确定B[I]先去的正确性。。。。于是,GG。

 http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1105

 

下面是代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const long long MAXN=50233;
 4 long long a[MAXN];
 5 long long b[MAXN];
 6 long long n,k;
 7 void init()
 8 {
 9     cin>>n>>k;
10     for(int i=0;i<n;++i)
11     {
12         cin>>a[i]>>b[i];
13     }
14     sort(a,a+n);
15     sort(b,b+n);
16 }
17 long long calculate(long long key)
18 {
19     long long ret=0;
20     for(int i=0;i<n;++i)
21     {
22         long long tar=key/a[i];
23         if(tar*a[i]!=key)tar++;
24         long long pos=lower_bound(b,b+n,tar)-b;
25         ret+=pos;
26     }return ret;
27 }
28 long long step(long long a,long long b,long long target)
29 {
30     if(a==b-1)return a;
31     long long mid=(a+b)/2;
32     long long res=calculate(mid);
33     if(res<=target)return step(mid,b,target);
34     else return step(a,mid,target);
35 
36 }
37 int main()
38 {
39     cin.sync_with_stdio(false);
40     init();
41     cout<<step(a[0]*b[0],a[n-1]*b[n-1]+1,n*n-k);
42     return 0;
43 }

代码其实很好写。。。但是因为没带电脑等等原因,这份代码差不多是大的第四遍了。。。。。。

 另外就是发现了一点,加了输入优化反而更慢了,下面是加了输入优化的AC代码:

#include<bits/stdc++.h>
using namespace std;
const long long MAXN=50233;
long long a[MAXN];
long long b[MAXN];
long long n,k;
void read_(long long &ret)
{
    ret=0;
    char c=0;
    while((c<'0'||c>'9'))c=getchar();
    while(c>='0'&&c<='9')ret*=10,ret+=c-'0',c=getchar();
}
void init()
{
//    cin>>n>>k;
    read_(n);
    read_(k);
    for(int i=0;i<n;++i)
    {
    //    cin>>a[i]>>b[i];
    read_(a[i]);
    read_(b[i]);
    }
    sort(a,a+n);
    sort(b,b+n);
}

long long calculate(long long key)
{
    long long ret=0;
    for(int i=0;i<n;++i)
    {
        long long tar=key/a[i];
        if(tar*a[i]!=key)tar++;
        long long pos=lower_bound(b,b+n,tar)-b;
        ret+=pos;
    }return ret;
}
long long step(long long a,long long b,long long target)
{
    if(a==b-1)return a;
    long long mid=(a+b)/2;
    long long res=calculate(mid);
    if(res<=target)return step(mid,b,target);
    else return step(a,mid,target);

}
int main()
{    
    cin.sync_with_stdio(false);
    init();
    cout<<step(a[0]*b[0],a[n-1]*b[n-1]+1,n*n-k);
    return 0;
}

 

posted @ 2017-08-18 14:19  六花的邪王真眼  阅读(300)  评论(0编辑  收藏  举报