递归DP方法

SPOJ SCUBADIV - Recursive DP Approach

本文关键字:方法 DP 递归      更新时间:2023-10-16

我一直在尝试解决http://www.spoj.com/problems/SCUBADIV/问题在SPOJ。我已经提出了一个递归的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)。方法正确吗?

这段代码有问题:

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

memset()函数设置一个字节模式,而不是一个值。由于inf是一个大于一个字节的值,它实际上是执行inf % 256并将dp[][][]中的所有字节初始化为该值。由于dp[][][]的基本类型是int,所以将4个字节设置为相同的字节值是不可预料的。

inf的情况下,99999999的字节值将是0xff,因此dp[][][]中的所有int将被设置为-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只能用于将字节设置为给定值。你的决定不像你想的那样。此外,如果执行到if链的末尾,递归过程将返回垃圾。

手工编写一个小示例。在调试器中运行代码或插入printf s以显示正在发生的事情。找出它的实际执行与你的手工计算有什么不同

是的,可以用递归方法解决这个问题,但这不是怎么做的。代码有多个问题,"显然"它将返回-1。我要回答的问题是:告诉我这段代码的一些错误之处。

  1. dp这样的变量名模糊了代码的含义。给他们起个有意义的名字!
  2. 不要调整矢量的大小并读入指针。读取值并将其压入向量。
  3. 打印出数据,以确保读取正确。
  4. memset函数填充字节,在本例中为-1。使用循环初始化int。
  5. 第一个if语句只能返回0或-1(或inf一旦你修复了init)。因为它是其他代码将不会被执行。
  6. dp赋值是无效的,因为它是自动存储的(在堆栈上)。
  7. 我不理解if语句链。解释它们。
  8. 没有else,所以函数可以掉到末尾。

最好重写,调试,如果它仍然不能工作,返回一些我们可以读的