SPOJ SCUBADIV - 递归DP方法

时间:2014-08-23 02:49:15

标签: c++ algorithm recursion dynamic-programming

我一直试图在SPOJ解决http://www.spoj.com/problems/SCUBADIV/问题。我想出了一个递归的DP解决方案。

我正在使用带有三维阵列的背包方法来存储气瓶数量,所需的氧气重量和氮气重量。在每个递归步骤中,我检查尚未填充的氧气和氮气的量。如果它是负数,那就好于零。

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

#define inf 99999999

int n;
vector<int> o;
vector<int> ni;
vector<int> w;
int ow;
int nw;
int knapsack(int n,int ow,int nw);  // n - number of cylinders,ow-wt. of oxygen
                                    // nw-wt. of nitogen.
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int i;
        scanf("%d %d",&ow,&nw);
        scanf("%d",&n);
        o.resize(n);
        ni.resize(n);
        w.resize(n);
        for(i=0;i<n;i++)
            scanf("%d%d%d",&o[i],&ni[i],&w[i]); // o[i] storing wt. of oxygen cylinders
        int res =  knapsack(n,ow,nw);          //ni[i] storing wt. of nitrogen cylinders
        printf("%d",res);
    }
    return 0;   
}


int knapsack(int n,int ow,int nw){
    int dp[n+1][ow+1][nw+1];
    memset(dp,inf,sizeof (dp));  //setting value of array to inf to get minimum weight
    int i;
    for(i=0;i<n;i++)
        dp[i][0][0]=0;

    if(dp[n][ow][nw]!= inf)
        return dp[n][ow][nw];
    else if (ow - o[n-1]>=0 && nw - ni[n-1]>=0)
        return dp[n][ow][nw]= min(knapsack(n-1,ow,nw),w[n-1]+knapsack(n-1,ow-o[n-1],nw-ni[n-1]));
    else if(ow -o[n-1]<0 && nw - ni[n-1] >=0)
        return dp[n][ow][nw]=min(knapsack(n-1,0,nw),w[n-1]+knapsack(n-1,0,nw-ni[n-1]));
    else if(ow-o[n-1]>=0 && nw-ni[n-1]<0)
        return dp[n][ow][nw]=min(knapsack(n-1,ow,0),w[n-1]+knapsack(n-1,ow-o[n-1],0));
    else if(ow-o[n-1]<0 && nw-ni[n-1]<0)
        return dp[n][ow][nw]= knapsack(n-1,0,0); 
}

此代码未给出所需的结果(它给出了-1)。方法是否正确?

3 个答案:

答案 0 :(得分:1)

此代码存在问题:

int dp[n+1][ow+1][nw+1];
memset(dp,inf,sizeof (dp));

memset()函数设置字节模式,而不是值。由于inf是一个大于一个字节的值,因此它基本上执行inf % 256并将dp[][][]中的所有字节初始化为该值。 dp[][][]属于基类型int,这进一步复杂化,因此设置为相同字节值的4个字节是意外的。

如果inf的值为99999999,则字节值将为0xff,因此int中的所有dp[][][]都将设置为-1

我不知道这是否是预期的,但看起来这可能是个错误。

答案 1 :(得分:1)

设M(x,O,N)是通过仅从气缸1到x中选择将提供O升氧气和N升氮气的气缸的最小重量。设O(x),N(x)和W(x)分别是第x个气缸中可用的氧气和氮气的量以及气缸的重量。然后我们要么选择使用第x个圆柱,要么我们不选择:

M(x, O, N) = min( W(x) + M(x - 1, O - O(x), N - N(x)), M(x - 1, O, N) )

当我们根本没有气瓶时会发生基本情况。

M(0, O, N) = 0 if O <= 0 and N <= 0, infinity otherwise

我不会阅读您的未格式化,加密编写的代码,以确定它是否正确实现了这一点。我会说memset只能用于将 bytes 设置为给定值。你的电话没有按你的想法行事。此外,如果执行到达if链的末尾,则递归过程将返回垃圾。

手工制作一个小例子。在调试器中或插入printf运行代码以显示正在进行的操作。弄清楚它的实际执行偏离你的手计算。

答案 2 :(得分:0)

是的,可以通过递归方法解决此问题,但这不是如何做到的。代码存在多个问题,显然&#39;它会返回-1。我将尝试回答的问题是:告诉我这段代码有些问题。

  1. dp之类的变量名称掩盖了代码的含义。给他们有意义的名字!
  2. 不要调整矢量大小并读入指针。读取值并将它们推送到矢量上。
  3. 打印出数据以确保您正确阅读。
  4. memset函数填充字节,在本例中为-1。使用循环初始化整数。
  5. 第一个if语句只能返回0或-1(或修复init后的inf)。因为它是其他代码将不会被执行。
  6. 将值分配到dp无效,因为它位于自动存储中(在堆栈上)。
  7. 我不理解if语句链。解释一下。
  8. 没有else因此功能可能会失效。
  9. 最好重写,调试,如果仍然没有工作,我们可以阅读。