C ++中的分治算法

时间:2017-01-24 06:15:24

标签: c++ algorithm divide-and-conquer

在某些在线评判中存在这样一个问题,即我不知道如何被接受。

问题就像第一行包含两个数字

N (0 < N < 2^18) 
M (0 < M < 2^20)

第二行包含N个数字

ai (0 < ai < 2^40)

问题是有多少X满足:

M = floor(X/a1) + floor(X/a2) + ... + floor(X/an)

我天真的解决方案:

#include<bits/stdc++.h>
using namespace std;

long long n,m,i,j,haha,sum;
int main()
{
    cin >> n >> m;
    haha = 0;
    long long ar[n+5];
    for(i = 0; i < n; i++) cin >> ar[i];
    sort(ar,ar+n);
    for(i = ar[0]+1; i < m*ar[0]; i++){
        sum = 0;
        for (j = 0; j < n; j++) sum += i/ar[j];
        if (sum == m) haha += 1;
        else if (sum >= m) break;
    }
    cout << haha << endl;
}

UPDATE1: 我的二进制搜索解决方案(仍未通过时间限制):

#include<bits/stdc++.h>
using namespace std;

long long n,m,i,l,r,mid,ans,tmp,cnt,haha;
long long ar[2621440];
long long func(long long x){
    haha = 0;
    for (i = 0; i < n; i++) haha += x/ar[i];
    return haha;
}

int main()
{
    cin >> n >> m;
    for(i = 0; i < n; i++) cin >> ar[i];
    sort(ar,ar+n);
    l = ar[0];
    r = ar[0]*m;
    mid = (l+r)/2;
    tmp = func(mid);
    while (tmp != m){
        mid = (l+r)/2;
        tmp = func(mid);
        if (l == r) break;
        if (tmp < m) l = mid+1;
        else if (tmp > m) r = mid-1;
        else break;
    }
    ans = 0;
    if (tmp == m) ans += 1;
    cnt = mid;
    while (func(cnt-1) == m){
        ans += 1;
        cnt -= 1;
    }
    cnt = mid;
    while (func(cnt+1) == m){
        ans += 1;
        cnt += 1;
    }
    cout << ans << endl;
}

3 个答案:

答案 0 :(得分:1)

<强>更新

使用二进制搜索方法,这是我的新代码:

[range.first, range.second)

核心功能是M = floor(X/a1) + floor(X/a2) + ... + floor(X/an) 功能,它采用可能的范围(floor(X/a1),因此第二个是范围的一部分)并缩小范围以便所有元素都在范围满足条件。它首先迭代地调整范围界限,直到范围的中间是结果的一部分,或者直到它清楚地表明范围内没有结果。然后,如果有任何结果,则递归检查找到的结果下方和上方的子范围,以便检索整个结果范围的范围。

版本1

您只处理大于零的正数。

floor(X1/ai) <= floor(X2/ai)

对于每个子词X1 < X2,如果X,则M。因此,导致floor(X1/ai) == floor(X2/ai)的唯一可能i值为ai所有ai(或所有X1=k*ai)。

对于每个X2=k*ai+(ai-1),对于某些k,这恰好是k*min(ai)的范围,直到(k+1)*min(ai)

这意味着,如果存在任何解决方案,对于某些0 < k <= m,X值的范围将介于// compute X/ai sum long long summarize(long long ar[], long long n, long long X) { long long sum = 0; for (long long i = 0; i < n; i++) { sum += X/ar[i]; } return sum; } int main() { int n, m; cin >> n >> m; long long *ar = new long long[n]; long long ar_min = LLONG_MAX; for(long long i = 0; i < n; i++) { cin >> ar[i]; ar_min = min(ar[i], ar_min); } // lowest possible k long long k = m / (ar_min * n); // get the value k for a possible range of X values for (; k <= m; k++) { auto x = ar_min * (k + 1); long long sum = summarize(ar, n, x); if (sum > m) { break; } } long long X_min = k * ar_min, X_max = (k + 1) * ar_min; long long result = 0; // count possible X values for (long long x = X_min; x < X_max; x++) { long long sum = summarize(ar, n, x); if (sum == m) { ++result; } else if (sum > m) { break; } } cout << result << endl; } public class Student { public int Id {get; set;} public string Name{get; set;} public ICollection<Course> Courses {get; set;} } public class Course { public int Id {get; set;} public string Name {get; set;} public ICollection<Student> Students {get; set;} } 之间。

因此,首先获得可能结果的范围,然后仅在该范围内检查各个值可能是值得的。

结果算法:

var studentCourses = from s in context.Students join c in context.Courses on s.Id equals ?? what?

它比我最初预期的要复杂一点。我希望它还有一些改进。

答案 1 :(得分:0)

我认为预期的解决方案是二元搜索。

定义f(x) = sum_i f(x/a_i)。在不失一般性的情况下,假设a_i以紧急顺序给出。

显然,

  • f(0) = 0 < M
  • f(M*a_1) ≥ M
  • f(x) ≥ f(y) if x≥y

因此,您可以执行二进制搜索,以找到x的最低值,f(x) = Mstart = 0end = M*a_1作为二进制搜索的初始限制。

要查找x的上限,请执行另一个二进制搜索,或者只是循环遍历数组中的所有值,以查找最小的y,使某些floor(y/ai) > floor(x/ai) i

答案 2 :(得分:0)

接受(最后)使用这个代码使用两个二进制搜索(每个用于下限和上限):

#include<bits/stdc++.h>
using namespace std;

long long n,m,i,l,r,mid1,mid2,ans,tmp,cnt,haha,k;
long long ar[26214400];
long long func(long long x){
    haha = 0;
    for (k = 0; k < n; k++) haha += x/ar[k];
    return haha;
}

int main()
{
    cin >> n >> m;
    for(i = 0; i < n; i++) cin >> ar[i];
    sort(ar,ar+n);
    l = ar[0];
    r = ar[0]*m;
    mid1 = (l+r)/2;
    tmp = func(mid1);
    while (l < r){
        mid1 = (l+r)/2;
        tmp = func(mid1);
        if (tmp < m) l = mid1+1;
        else if (tmp > m) r = mid1-1;
        else r = mid1-1;
    }
    mid1 = l; //lower bound
    l = ar[0];
    r = ar[0]*m;
    mid2 = (l+r)/2;
    tmp = func(mid2);
    while (l < r){
        mid2 = (l+r)/2;
        tmp = func(mid2);
        if (tmp < m) l = mid2+1;
        else if (tmp > m) r = mid2-1;
        else l = mid2+1;
    }
    mid2 = r; //upper bound
    while (mid1 <= mid2 and func(mid1) != m) mid1 += 1;
    while (mid2 >= mid1 and func(mid2) != m) mid2 -= 1;
    ans = mid2-mid1+1;
    cout << ans << endl;
}