二分例题 51nod

例题1  

1010 只包含因子2 3 5的数

http://www.51nod.com/Challenge/Problem.html#problemId=1010

K的因子中只包含2 3 5。满足条件的前10个数是:2,3,4,5,6,8,9,10,12,15。

所有这样的K组成了一个序列S,现在给出一个数n,求S中 >= 给定数的最小的数。

例如:n = 13,S中 >= 13的最小的数是15,所以输出15。

 

输入

第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000)
第2 - T + 1行:每行1个数N(1 <= N <= 10^18)

输出

共T行,每行1个数,输出>= n的最小的只包含因子2 3 5的数。

输入样例

5
1
8
13
35
77

输出样例

2
8
15
36
80
题解 :这个题目用到二分,二分之前需要预处理一个数组
定理:K=2^x*3^y*5^z即k是只包含2,3,5,的因子的数
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf =1e18+10000;//这里的范围要比1e8大1000左右
const int N=1E6+7;
ll arr[N];
int pos=0;
void inint(){
    ll i, j, k;
    for(i=1;i<inf;i*=2)
        for(j=1;j*i<inf;j*=3)
            for(k=1;k*i*j<inf;k*=5)
                arr[pos++]=i*j*k;
}
int main(){
    inint();
    sort(arr,arr+pos);
    int t;
    scanf("%d",&t);
    while(t--){
        ll n;
        cin>>n;
        ll ans;
        int left=1;
        int right=pos-1;
        while(left<=right){
            int mid=(left+right)/2;
            if(arr[mid]>=n){
                ans=arr[mid];
                right=mid-1;
            }
            else {
                left=mid+1;
            }
        }
        cout<<ans<<endl;
    }
    
    return 0;
} 

例题2

1267 4个数和为0

http://www.51nod.com/Challenge/Problem.html#problemId=1267

给出N个整数,你来判断一下是否能够选出4个数,他们的和为0,可以则输出"Yes",否则输出"No"。
 

输入

第1行,1个数N,N为数组的长度(4 <= N <= 1000)
第2 - N + 1行:A[i](-10^9 <= A[i] <= 10^9)

输出

如果可以选出4个数,使得他们的和为0,则输出"Yes",否则输出"No"。

输入样例

5
-1
1
-5
2
4

输出样例

Yes
题解:要找4个数,这四个数的和为0,我们先确定好两个数,然后再二分查找另外两个数
#include<bits/stdc++.h>
using namespace std;
const int N=1000+7;
int arr[N];
int main(){
    int t;
    cin>>t;
    for(int i=0;i<t;i++) scanf("%d",&arr[i]);
    sort(arr,arr+t);
    bool flag=false ;
    for(int i=0;i<t;i++){
        for(int j=i+1;j<t;j++){
            for(int k=j+1;k<t;k++){
                int ans=arr[i]+arr[j]+arr[k];
                int left=k+1;
                int right=t-1;
                while(left<=right){
                    int mid=(left+right)/2;
                    if(arr[mid]+ans>0){
                        right=mid-1;
                    }
                    else if(arr[mid]+ans<0){
                        left=left+1;
                    }
                    else {
                        flag=true;
                        break;
                    }
                }
                if(flag) break;
            }
            if(flag) break;
        }
        if(flag) break;
    }
    if(flag) puts("Yes");
    else puts("No");
    return 0;
}

例题3

1105 第K大的数

http://www.51nod.com/Challenge/Problem.html#problemId=1105

数组A和数组B,里面都有n个整数。

数组C共有n^2个整数,分别是:

A[0] * B[0],A[0] * B[1] ...... A[0] * B[n-1]

A[1] * B[0],A[1] * B[1] ...... A[1] * B[n-1] 

...... 

A[n - 1] * B[0],A[n - 1] * B[1]  ......  A[n - 1] * B[n - 1]

是数组A同数组B的组合,求数组C中第K大的数。

例如:

A:1 2 3,B:2 3 4。

A与B组合成的C为

         A[0]  A[1]  A[2]

B[0]     2      3      4

B[1]     4      6      8

B[2]     6      9     12

共9个数。

 

输入

第1行:2个数N和K,中间用空格分隔。N为数组的长度,K对应第K大的数。(2 <= N <= 50000,1 <= K <= 10^9)
第2 - N + 1行:每行2个数,分别是A[i]和B[i]。(1 <= A[i],B[i] <= 10^9)

输出

输出第K大的数。

输入样例

3 2
1 2
2 3
3 4

输出样例

9
题解 二分套二分。传统的二分都是对排好序的数组的下标进行处理,这里不一样,我们二分的left为最小值,right为最大值,然后二分这个区间,同时判断大于mid的个数,当大于mid的个数为m-1时,就是答案。但是不一定是最优的答案,所以要继续增大,继续查找
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N= 50000+7;
ll arr1[N],arr2[N];
int n,m;
ll sum(ll x){
    ll sum1=0;
    for(int i=0;i<n;i++){
        int left=0;
        ll ans=n;
        int right=n-1;
        while(left<=right){
            int mid=(left+right)/2;
            if(arr1[i]*arr2[mid]>x){
                right=mid-1;
                ans=mid;
            }
            else {
                left=mid+1;
            }
        }
        sum1+=n-ans;
    }
    return sum1;
}
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++)    scanf("%lld%lld",&arr1[i],&arr2[i]);
    sort(arr1,arr1+n);
    sort(arr2,arr2+n);
    ll left=arr1[0]*arr2[0];
    ll ans=0;
    ll right=(1ll*arr1[n-1])*(1ll*arr2[n-1]); 
    while(left<=right){
        ll mid=(left+right)/2;
        ll x=sum(mid);
//        if(x==m-1){
//            ans=mid;
//            right=mid-1;
//        }  //这里这样写有点不对,因为我们的sum函数只是保存了比mid大的数的个数,但是当出现两个相等的数时,比他们大的数的数目是一样的,但是这两个数的位置不一样,一个在前边,一个在后边
        if(x<m){ 
            ans=mid;
            right=mid-1;
        }
        else {
            left=mid+1;
        }
    }
    cout<<ans<<endl;
    return 0;
}


猜你喜欢

转载自www.cnblogs.com/Accepting/p/11507735.html
今日推荐