建议优化代码以在SPOJ上传递TLE

时间:2012-03-22 16:33:30

标签: c++ algorithm optimization data-structures

我正在尝试解决类似这样的问题:

我给了n个数字(1 <= n <= 10 ^ 5)。我必须写出左边所有数字的总和,这些数字小于当前数字并重复所有n个数字的过程。然后我必须找到所有先前获得的和的总和。(每个数字N,0 <= N <= 10 ^ 6)。

例如,

1 5 3 6 4
less1 less5 less3 less6 less4
 (0) + (1) + (1)+(1+5+3)+(1+3)
  0  +  1  +  1  +  9  +  4
= 15

这个问题的一个简单的解决方案是运行两个循环,并且对于每个给定的数字,找到小于该数字的所有数字的总和,最后给出这些和的总和作为输出。时间复杂度为O(n ^ 2)。

我认为使用二进制索引树(Fenwick树)可以更好地解决这个问题的O(nlogn)解决方案。 对于每个数字,我将在全局数组a中添加每个数字并执行两个明显的BIT操作。我认为该算法的时间复杂度为O(nlogn),如果为真,则明显优于之前的O(n ^ n ^ 2)。

我已用C ++实现了代码。

#include<iostream>
#include<cstdio>

using namespace std;

#define max 1000001

long int a[max];

void add(long int v,int idx){
while(idx<max){
    a[idx] += v;
    idx += (idx & -idx);
}
}

long int sum(int idx){
long int s=0;
while(idx>0){
    s += a[idx];
    idx -= (idx & -idx);
}
return s;
 }

 int main()
 {
int t;
scanf("%d",&t);
for(int w=0;w<t;w++){
    int n;
    scanf("%d",&n);

    for(int i=0;i<max;i++)
        a[i]=0;

    int arr[n];
    for(int i=0;i<n;i++)
        scanf("%d",&arr[i]);

    long long res=0;

    for(int i=0;i<n;i++){
        if(arr[i]!=0){
           add(arr[i],arr[i]);
           res += (sum(arr[i]-1));
                     }  
    }

    printf("%lld\n",res);
}

return 0;
}

我有两个问题:

首先,我做得对吗? /我的逻辑是否正确?

第二,如果我把时间复杂度定为O(nlogn)那么为什么它运行缓慢?你能帮助我进一步优化吗?

已接受1.41秒。同时我更新了我最终接受的代码。有关优化的建议吗?

基于这些评论,我尝试了自己的功能,以实现更快的I / O,但仍然没有按照我的方式进行。这是我的快速I / O功能:

 inline int read(){
char c=getchar_unlocked();
int n=0;
while(!(c>='0' && c<='9'))
    c=getchar_unlocked();

while(c>='0' && c<='9'){
    n=n*10 + (c-'0');
    c=getchar_unlocked();   
}
return n;
 }

这是问题的链接:

http://www.spoj.pl/problems/DCEPC206/

如果有人能够解决它,请告诉我。 感谢。

2 个答案:

答案 0 :(得分:2)

这是另一种方法:问题类似于计算反转,除非您必须对负责生成反转的元素求和。我们可以使用merge sort来解决这个问题。像这样修改合并函数:

merge(left, middle, right, array)
  temp = new array
  k = 0, i = left, j = middle + 1

  while i <= middle and j <= right
    if array[i] < array[j]
      temp[k++] = array[i]
      // array[i] is also smaller than all array[j+1], ..., array[right]
      globalSum += array[i] * (right - j + 1)
    else
      // same as the classical function

直观地说,我会说递归mergesort比BIT解决方案慢,但谁知道呢?试一试。

编辑:这是AC:

    #include<stdio.h>
#include <iostream>

using namespace std;

#define max 100001

int n;
long long res = 0;

int temp[max];
int arr[max];
void merge(int left, int m, int right)
{
    int k = 0;
    int i = left, j = m + 1;
    while (i <= m && j <= right)
        if (arr[i] < arr[j])
        {
            temp[k++] = arr[i];
            res += (long long)(right - j + 1) * arr[i++];
        }
        else
            temp[k++] = arr[j++];

    while (j <= right)
        temp[k++] = arr[j++];
    while (i <= m)
        temp[k++] = arr[i++];

    for (int i = 0; i < k; ++i)
        arr[left + i] = temp[i];
}

void sort(int left, int right)
{
    if (left < right)
    {
        int m = left + (right - left) / 2;
        sort(left, m);
        sort(m + 1, right);
        merge(left, m, right);
    }
}

int main()
{
    int t;
    scanf("%d", &t);
    for(int w=0;w<t;w++)
    {
        scanf("%d", &n);
        for(int i=0;i<n;i++)
            scanf("%d", &arr[i]);

        res=0;
        sort(0, n - 1);

        printf("%lld\n",res);
    }

    return 0;
}

答案 1 :(得分:2)

我认为你的方法很好。我玩过这个小小的玩具并没有提出任何比你拥有的更好的东西。

但是代码中有一些错误。有几个地方遭遇整数溢出。你应该改为:

long long a[max];

long long sum(int idx){
long long s=0;

更明显的错误是您汇总的数字小于或等于当前数字。要解决此问题,您可以添加第二个全局数组来跟踪每个值的计数:

int b[max];
...
...
    for(int i=0;i<max;i++)
        a[i]=b[i]=0;
    ...
    ...
        res += (sum(idx)-(++b[idx]*val));

可能有一种更有效的方法来修复该错误,但总体而言,它似乎仍然是一种快速解决方案。