【数位统计】之【spoj1433 KPSUM】

【spoj1433】KPSUM

来源

高逸涵《数位计数问题解法研究》

由于自己的数位计数类的问题实在太差了,所以把例2用markdown抄写并补充了一遍。

题意

将写在纸上,然后在相邻的数字间交替插入“+”和“-”,求最后的结果。例如当时,答案为:

分析

这是一道稍微复杂一点的数位计数问题。

我们首先探查数位确定,所有数字*的情况。

不妨设第一位从”+”开始。

若数位个数为偶数,以6位为例:

+0 -0 +0 -0 +0 -0
+0 -0 +0 -0 +0 -1
+0 -0 +0 -0 +0 -2
+9 -9 +9 -9 +9 -9

发现:

①每个数位的符号相同,奇数符号的数位和偶数符号的数位个数相等。

②每个数位中每个数出现的次数相同。

所有数位的所有数的和为0。

若数位个数为奇数,以5位为例:

+0 -0 +0 -0 +0
-0 +0 -0 +0 -1
+0 -0 +0 -0 +2
-0 +0 -0 +0 -3
+9 -9 +9 -9 +8
-9 +9 -9 +9 -9

相邻两行的和为-1。

之前我们这样认定:

不妨设第一位从”+”开始。

然而第一位不一定是+,这跟总位数有关。

注意:当前我们考虑的只是后面位对答案的贡献,前缀对答案的贡献并没有计算。

(1)当为偶数时

①当为偶数时,后面位数的贡献为0;

②当为奇数时,从第位开始贡献为0,所以我们只需要考虑第位的贡献。

第位的全是负号,贡献为:



(2)当为奇数时,相邻两行和为-1,总共有个数,所以有行,所以贡献为:

于是我们写出函数GetSum1。

LL GetSum1(int n,int k)
//n为*位个数,k为总位数
{
    if (k%2==0)
    {
        if (n%2==0) return 0;
        else
        {
            LL d=-45;
            rep(i,1,n-1) d*=10;
            return d;
        }
    }
    else
    {
        LL d=-1;
        rep(i,1,n) d*=10;
        return d/2;
    }
}

接下来,考虑带前缀的情况。总位数=前缀位+*位。

(1)当总位数为偶数时,前缀符号不变,乘上总行数即可。

(2)当总位数为奇数时,前缀两两相消。

依照以上分析编写GetSum2。

LL GetSum2(LL prefix,int n)
//prefix为前缀,n为*位个数
{
    int d=0,t=1;
    LL p=prefix,presum=0;
    while (p>0)
    {
        presum+=(p%10)*t;
        p/=10;
        d++;
        t=-t;
    }
    presum*=-t;
    rep(i,1,n) presum*=10;
    LL ret=GetSum1(n,n+d);
    if ((d+n)%2==0) ret+=presum;
    return ret;
}

沿用上例的思路,,再有了上述两个函数之后,我们继续将整个区间划分为若干段,分别利用上述函数求和,这里不再重复叙述。

小结

通过对问题从简单到复杂的层层递进分析,逐步将程序实现,使得一个原本比较复杂的问题轻松被解决。程序编写过程中思路明确,程序模块化合理,每个模块功能明确,并且单独可以通过check函数进行检验。使得出错的可能性大大降低。

虽然整体代码量有所增加,但由于思考的时间减少,代码编写时间甚至还会缩短。

上一篇:php单链表实现的代码


下一篇:Dubbo -- 系统学习 笔记 -- 示例 -- 多注册中心