Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 用二进制搜索法求n*m乘法表的第k大数_C++_Algorithm_Binary Search - Fatal编程技术网

C++ 用二进制搜索法求n*m乘法表的第k大数

C++ 用二进制搜索法求n*m乘法表的第k大数,c++,algorithm,binary-search,C++,Algorithm,Binary Search,因此,我试图解决这个问题: 冠军比松不仅迷人,他还非常聪明 当我们中的一些人在学习乘法表时,冠军比松以他自己的方式玩得很开心。冠军比松画了一幅n × m乘法表,其中第i行和第j列交点上的元素等于i·j(表中的行和列从1开始编号)。然后有人问他:表中第k个最大的数字是什么?冠军比松的回答总是正确而迅速。你能重复他的成功吗 考虑给定的乘法表。如果以非递减顺序写出表中的所有n·m数字,则写出的第k个数字称为第k个最大数字 输入 单行包含整数n、m和k(1 ≤ N M ≤ 5·105; 1 ≤ K ≤ 

因此,我试图解决这个问题:

冠军比松不仅迷人,他还非常聪明

当我们中的一些人在学习乘法表时,冠军比松以他自己的方式玩得很开心。冠军比松画了一幅n × m乘法表,其中第i行和第j列交点上的元素等于i·j(表中的行和列从1开始编号)。然后有人问他:表中第k个最大的数字是什么?冠军比松的回答总是正确而迅速。你能重复他的成功吗

考虑给定的乘法表。如果以非递减顺序写出表中的所有n·m数字,则写出的第k个数字称为第k个最大数字

输入 单行包含整数n、m和k(1 ≤ N M ≤ 5·105; 1 ≤ K ≤ n·m)

输出 打印n中的第k个最大数字 × m乘法表

我所做的是,我应用了从1到
n*m
的二进制搜索,查找元素数正好小于它的数。为此,我编写了以下代码:

using namespace std;
#define ll long long
#define pb push_back
#define mp make_pair
ll n,m;
int f (int val);
int min (int a, int b);
int main (void)
{
    int k;
    cin>>n>>m>>k;
    int ind = k;
    ll low = 1LL;
    ll high = n*m;
    int ans;
    while (low <= high)
    {
        ll mid = low + (high-low)/2;
        if (f(mid) == k)
            ans = mid;
        else if (f(mid) < k)
            low = mid+1;
        else
            high = mid-1;
    }
    cout<<ans<<"\n";
    return 0;

}

int f (int val)
{
    int ret = 0;
    for ( int i = 1; i <= n; i++ )
    {
        ret = ret + min(val/i,m);
    }
    return ret;
}

int min (int a, int b)
{
    if (a < b)
        return a;
    else
        return b;
}
我的输出结果是0


我正在学习二进制搜索,但我不知道这个实现哪里出了问题。任何帮助都将不胜感激

忽略二进制搜索不是最快的方法这一事实,您仍然想知道为什么它是不正确的

首先要非常清楚你想要什么以及你的f会返回什么:

查找正好有k个元素小于它的数

不!您正在寻找k个元素小于或等于它的最小数目。函数
f(X)
返回小于或等于X的元素计数

因此,当f(X)返回的值太小时,您知道X必须至少大于1,因此
low=mid+1
是正确的。但是当f(X)返回的值太大时,X可能是完美的(可能是一个元素在表中多次出现)。相反,当f(X)返回正确的数字时,X可能仍然太大(X可能是表中出现零次的值)

因此,当f(X)不是太小时,最好的方法是
high=mid
not
high=mid-1

while (low < high)
{
    ll mid = low + (high-low)/2;
    if (f(mid) < k)
        low = mid+1;
    else
        high = mid;
}
while(低<高)
{
ll中=低+(高-低)/2;
if(f(mid)
请注意,low永远不会得到>high,所以当它们相等时停止,并且我们不会一路抓住
ans
。相反,在结尾处,低==高==答案

比赛规定了1秒的时间限制。在我的电脑上,你的代码在不到一秒钟的时间内解决了最大尺寸的问题。但我不确定评判计算机是否有那么快

编辑:
int
对于问题的最大大小来说太小,因此无法从f:
n、 m和i都适合32位,但是f()的输入和输出以及k、ret、low和high都需要保存高达2.5e11的整数;
import java.util.*;
public class op {
    static int n,m;
    static long k;
    public static void main(String args[]){
        Scanner s=new Scanner(System.in);
        n=s.nextInt();
        m=s.nextInt();
        k=s.nextLong();
        long start=1;
        long end=n*m;
        long ans=0;
        while(end>=start){
            long mid=start+end;
            mid/=2;
            long fmid=f(mid);
            long gmid=g(mid);
            if(fmid>=k && fmid-gmid<k){
                ans=mid;
                break;
            }
            else if(f(mid)>k){
                end=mid-1;
            }
            else{
                start=mid+1;
            }
        }
        System.out.println(ans);

    }
    static long f (long val)
    {
        long ret = 0;
        for ( int i = 1; i <= n; i++ )
        {
            ret = ret + Math.min(val/i,m);
        }
        return ret;
    }
    static long g (long val)
    {
        long ret = 0;
        for ( int i = 1; i <= n; i++ )
        {
            if(val%i==0 && val/i<=m){
                ret++;
            }
        }
        return ret;
    }
    public static class Pair{
        int x,y;
        Pair(int a,int b){
            x=a;y=b;
        }
    }
}
公开课{ 静态int n,m; 静态长k; 公共静态void main(字符串参数[]){ 扫描仪s=新的扫描仪(System.in); n=s.nextInt(); m=s.nextInt(); k=s.nextLong(); 长启动=1; 长端=n*m; 长ans=0; while(结束>=开始){ 长中=开始+结束; mid/=2; 长fmid=f(中); 长gmid=g(中); 如果(fmid>=k&&fmid-gmidk){ end=mid-1; } 否则{ 开始=中间+1; } } 系统输出打印LN(ans); } 静态长f(长val) { 长ret=0;
对于(int i=1;i这与搜索无关,第k个最大数是从末尾开始写入的第k个元素。您可以使用模运算和标准运算直接计算它。如果您准确找到答案,则需要一个
中断
,否则情况将是一个无限循环。但根据您报告的症状,这不是唯一的情况bug.@Dese,但是如何编写这样的元素呢?复杂性将大于O(n^2),并且它不支持给定的限制。@JSF,即使打破了限制,它似乎也不起作用。@hans第一大元素是n*m,第二大元素是n*(m-1),第三个元素是n*(m-3)…当您到达行的开头时:n,它就是(n-1)*m根据定义。(参见2 X 3乘法表的示例,如果你真的在谈论最大元素,那么第四个是2:6 4 3 2,但它们的定义是关于书写顺序,所以答案是3)。我在谈论找到一个公式,使结果=f(n,m,k).请解释你的解决方案。OP做错了什么?
import java.util.*;
public class op {
    static int n,m;
    static long k;
    public static void main(String args[]){
        Scanner s=new Scanner(System.in);
        n=s.nextInt();
        m=s.nextInt();
        k=s.nextLong();
        long start=1;
        long end=n*m;
        long ans=0;
        while(end>=start){
            long mid=start+end;
            mid/=2;
            long fmid=f(mid);
            long gmid=g(mid);
            if(fmid>=k && fmid-gmid<k){
                ans=mid;
                break;
            }
            else if(f(mid)>k){
                end=mid-1;
            }
            else{
                start=mid+1;
            }
        }
        System.out.println(ans);

    }
    static long f (long val)
    {
        long ret = 0;
        for ( int i = 1; i <= n; i++ )
        {
            ret = ret + Math.min(val/i,m);
        }
        return ret;
    }
    static long g (long val)
    {
        long ret = 0;
        for ( int i = 1; i <= n; i++ )
        {
            if(val%i==0 && val/i<=m){
                ret++;
            }
        }
        return ret;
    }
    public static class Pair{
        int x,y;
        Pair(int a,int b){
            x=a;y=b;
        }
    }
}