SPOJ硬币DP和递归方法

时间:2016-07-08 14:02:33

标签: c algorithm segmentation-fault dynamic-programming

我最近开始解决DP问题,我遇到了硬币。我尝试使用带有memoization的DP解决它,如果我使用int数组(我猜)它可以正常工作。 这是我的方法(剩下几点修改):

#include <stdio.h>
#include <stdlib.h>
int dp[100000];
long long max(long x, long y)
{
   if (x > y)
      return x;
   else
      return y;
}
int main()
{
    int n,i;
    scanf("%d",&n);
    dp[0]=0;
    for(i=1;i<=n;i++)
    {
        dp[i]=max(i,dp[i/2] + dp[i/3] + dp[i/4]);
    }
    printf("%d\n",dp[n]);

    return 0;
}

但我不明白,一旦我使用长长阵列,我就会得到SIGSEGV。 我搜索过,似乎有一个我不理解的递归解决方案。 有人可以帮帮我吗?

1 个答案:

答案 0 :(得分:3)

限制为n<=10e9,其数组大小将始终导致内存溢出,因此SIGSEGV。什么是dp-array的类型并不重要。

您的代码中还有其他错误。首先,有一些测试用例,你必须阅读它们直到EOF。其次,由于限制为10e9,因此您循环n次!当然是TLE。

现在,对于递归解决方案,使用memoization:

首先,将答案值保存到数组中的10e6。将有助于节省时间。可以这样做:

long long dp[1000000] = {0};
for(int i = 1; i < 1000000; i++){
    dp[i] = max(i, dp[i/2] + dp[i/3] + dp[i/4]);
}

现在,对于任何输入n,请找到解决方案,

ans = coins(n);

coins功能实施为:

long long coins(long long n){
    if (n < 1000000)
        return dp[n];
    return coins(n/2) + coins(n/3) + coins(n/4);
}
  

为什么这种递归解决方案有效:

很明显,所有n >= 12的答案都是ans[n/2] + ans[n/3] + ans[n/4],因此对于n > 10e6,会返回。

递归的基本条件只是为了节省时间。您也可以将其返回0,但之后您将不得不处理角落案件。 (你明白我的观点)

确切代码:

#include<stdio.h>
long long dp[1000000] = {0};

long long max(long long a, long long b){
    return a>b?a:b;
}

long long coins(long long n){
    if (n < 1000000)
        return dp[n];
    return coins(n/2) + coins(n/3) + coins(n/4);
}

int main(){
for(long long i = 1; i < 1000000; i++){
    dp[i] = max(i, dp[i/2] + dp[i/3] + dp[i/4]);
}
long long n;
while(scanf("%lld",&n) != EOF){
    printf("%lld\n", coins(n));
}
return 0;
}