【问题标题】:How many numbers made out of N digits, have the sum S? (dynamic programming)有多少个由N个数字组成的数字,总和为S? (动态规划)
【发布时间】:2019-10-04 08:08:52
【问题描述】:

所以我无法理解你是如何做到这一点的。你输入像 N=2 和 S=3,这意味着有多少个由 2 位数字组成的数字的总和 =3?比如 12 => 1+2= 3;对于 N=2 和 S=3 ,有 3 个数字:12、30、21。

我不太了解动态编程。你应该如何看待这个算法和类似的算法?

【问题讨论】:

  • 试试谷歌。 geeksforgeeks.org/…
  • 第 1 步:确定哪些数字具有该和。第 2 步:创建这些数字的所有组合。将所有可行位置的零位填充到 N 位,如果该数字超过 N 位,则拒绝。
  • 动态编程就是在数据库中存储您已经完成的计算,并在需要使用它们的结果而不是再次计算时查找它们,因为查找在计算上比计算便宜.递归斐波那契的实现可以通过动态编程大大加快,但是这个问题......使用它似乎效率不高,也许......
  • 除了@PaulOgilvie 所写的内容之外,如果目标只是计算数字,则不需要全部生成。您可以使用组合数学。这可能就是要使用“动态编程”部分的地方。
  • ... 例如:如果你确定数字0、0、1、3之和为4,则总共有4位,2位非零,1位重复 2 次。所以这些数字的排列方式是 2 * 3! /2! (第一个数字的 2 个组合,乘以 3!其余 3 个数字的组合,除以 2!重复数字)。这些数字总共有 6 种排列方式:1003、1030、1300、3001、3010、3100。

标签: c dynamic-programming


【解决方案1】:

想想蛮力/回溯解决方案。你会怎么写,你需要哪些状态,哪些状态可以删除?

对于这个特殊问题,您可以做的是在从最高有效数字开始的位置插入一个数字,然后移动到下一个位置以跟踪总和。插入所有数字后,如果总和为零,则将答案加 1。

所以 Backtrack 解决方案将是这样的

void f(int pos,int sum) {
    if(pos == n) {
        if(!sum) ans += 1;
        return;
    }
    for(int i = 0 ; i < 10 ; i++)
        f(pos + 1 , sum - i);
}

如果您能提出 backTrack 解决方案,那么您已经完成了 90% 的工作。对于动态编程,您只需保存状态,这样您就不必重新计算状态两次。另外,考虑极端情况和基本情况。在我们的 backTrack 解决方案中,我们有一个错误。我们不应该在最高有效数字处插入 0。 修复所有这些代码应该看起来像这样

#define maxN 1000
#define maxSum 1000
#define i64 long long int
#define mod 1000000007

int n;
i64 dp[maxN][maxSum];

i64 f(int pos,int sum) {
        if(sum < 0) return 0;

        if(pos == n)
        {
                if(!sum) return 1;
                return 0;
        }


        if( dp[pos][sum] != -1)
                return dp[pos][sum];

        int lo = 0;
        if(!pos)
                lo = 1;

        i64 ans = 0;
        for(int i = lo ; i < 10 ; i++)
                ans += f(pos + 1, sum - i);

        ans %= mod;


         return dp[pos][sum] = ans;
}

int main()
{
        int s;

        cin >> n >> s;

        memset(dp,-1,sizeof dp);

        cout << f(0,s) << endl;


        return 0;
}

代码的时间复杂度为O(maxN * maxSum * 10)

您可以在线找到更优化的解决方案。但是一旦你了解了更多关于动态规划的知识,你就会意识到提出一个 dp 解决方案比其他解决方案更快、更容易。快乐编码。

【讨论】:

    猜你喜欢
    • 2021-11-15
    • 2018-12-16
    • 2011-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多