📜  C |运营商|问题27(1)

📅  最后修改于: 2023-12-03 15:13:47.674000             🧑  作者: Mango

C运营商问题27

介绍

C运营商问题27是一道经典的算法问题,涉及到数字的位数操作和特殊的乘法规则,需要用到一些数学技巧。问题的描述如下:

给定一个数n,求出满足以下条件的数m的个数:

  1. m大于n
  2. n的位数为k,m的位数为k,且m中不含0
  3. m可以通过n中的数字通过只能使用'+','-'和'*'来构造。

例如,当n=123,k=3时,满足条件的数有124、132、213、231、312、321共6个。

解法思路

首先,我们需要理解题目中给出的乘法规则。因为在运算符只有加减乘的情况下,我们就可以把每个数拆分成一个个位数来处理,从而实现构造m的目的。

设n为k位数,其各个数位的值分别为a1、a2、……、ak。 先看n中各数位间的关系,以a1、a2、a3为例:

  • a2 * a3 <= 9: 那么将a2和a3之间的乘符换成加符即可,也就是说,可以找到相对应的m。
  • a2 *a3 > 9: 那么a2和a3之间的乘符只能是乘号,则可以用a1和a2间的符号来构造1位和2位数,重复此过程直到最后一位,构建出m。

接着,我们需要考虑如何统计满足条件的m的个数。一个常用的方法是动态规划。

设f[i][j]表示第i位填入数字j时能构造出的不含0且大于n的数个数,最终答案即为f[k][0]~f[k][9]的和。

状态转移方程为:

  • 若a[i]*a[j]<=9,则f[i][j] += f[i+1][j+1]+f[i+1][j+2]+...+f[i+1][9]
  • 若a[i]*a[j]>9,则f[i][j] += f[i+1][0]+f[i+1][1]+...+f[i+1][9]

最后,我们将从高到低每一位的f值累加起来即可得到答案。

代码实现

以下为C++语言的代码实现:

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

#define int long long

const int MAXN = 20;

int f[MAXN][12];
int a[MAXN];
int len;

signed main() {
    int n; cin >> n;

    while (n) {
        a[++len] = n % 10;
        n /= 10;
    }

    for (int i = 0; i <= 9; ++i) f[len][i] = (i >= a[len]);

    for (int i = len - 1; i > 0; --i) {
        for (int j = 0; j <= 9; ++j) {

            int lim = j * a[i];
            for (int k = j; k <= 9 && k <= lim; ++k) {
                f[i][j] += f[i+1][k];
            }

            if (a[i+1] * j <= 9) continue;

            lim = 9;
            if (j * a[i+1] > 9) lim = a[i] - 1;
            for (int k = 0; k <= lim; ++k) {
                f[i][j] += f[i+1][k];
            }
        }
    }

    int ans = 0;
    for (int i = 1; i <= 9; ++i) {
        ans += f[1][i];
    }

    cout << ans << endl;

    return 0;
}

以上代码基本实现了解题思路中所述的动态规划方法。