前缀和

看到别人在群中讨论一道题,发现自己也不会做。后来才听到要用前缀和的方法进行求解。在此记录一下我所学到的知识。

题目内容

题目链接

题目分析

可能遇到这种问题,每个人首先想到的办法就是暴力求解,(假设a小于b)即算出从a到b每个数中1的个数,然后求和。像这样:

#include
using namespace std;

//cal函数求a中所包含的1的个数 
int cal(int a)
{
    int cnt = 0;
    while(a)
    {
        if(a%10 == 1)   cnt++;
        a /= 10;
    }
    return cnt;
}

int main()
{
    int a,b;
    while(scanf("%d%d",&a,&b) != EOF)
    {
        if(a>b)
        {
            int tmp = a;
            a = b;
            b = tmp;
        }

        int sum = 0;
        for(int i=a; i<=b; i++)
        {
            sum += cal(i);
        }
        printf("%d\n",sum);
    }   
}

但是这样的话会超时(Time Limit Exceed)。为什么呢?因为我们可以注意题目中的数据的范围:多组数据(不超过100000组),每组数据2个整数a,b.(1≤a,b≤1000000)。假设组数为T,a、b范围为N,计算每个数字中1的个数需要M次运算。那么时间复杂度就是O(T*N*M),T和N是很大的,T为1e5,N为1e6,显然这样是会超时的。
那么该如何处理呢?
这里就运用到了一种前缀和的思想。具体就是用一个全局数组sum[],用sum[i]来保存前i个数中所包含的1出现的次数之和 。而且提前算好sum[0]-sum[1e6]的值。那么每组数据就可以直接用sum[b]-sum[a-1]来计算。时间复杂度就是

O(1*T)+O(N*M) = O(N*M)
这样就不会超时。

我的代码

#include
using namespace std;

const int maxn = 1e6+1;
int sum[maxn];      //sum[i]表示前i个数中所包含的1出现的次数之和 

//cal函数求a中所包含的1的个数 
int cal(int a)
{
    int cnt = 0;
    while(a)
    {
        if(a%10 == 1)   cnt++;
        a /= 10;
    }
    return cnt;
}


int main()
{
    //求sum[0]-sum[1e6]的值
    sum[0] = 0; 
    for(int i=1; i<=1e6; i++)
    {
        sum[i] = sum[i-1] + cal(i);
    }  

    int a,b;
    while(scanf("%d%d",&a,&b) != EOF)
    {
        if(a>b)
        {
            int tmp = a;
            a = b;
            b = tmp;
        }
        printf("%d\n",sum[b]-sum[a-1]);
    } 
    return 0;
}

再看前缀和

我觉得前缀和的思想就是一种预处理,是一种以空间换时间的做法。就是先提前求得sum[i],保存下来,以后计算的时候直接用。动态规划就经常用到这样的手段。

你可能感兴趣的:(数据结构与算法)