c++ - ScubaDiv编程,输出时出现逻辑错误

标签 c++ algorithm dynamic-programming

这个问题与潜水员或许多潜水员有关,这些潜水员必须带入装有氧气和氮气的钢瓶,钢瓶也有自重。使用动态编程,我们必须提供一种解决方案,该解决方案告诉潜水员使用所需的Oxy和Nitro可获得最佳重量(以最大化潜水员在水下的时间)

输入如下:

1          //the number of divers
5 60       // ox=5 and ni=60 , the amonut of OX and NI the diver needs
5          //the number of cylinders to choose - n.
3 36 120   // 1st cyllinder => ox=3 / nit=36 / weight = 120
10 25 129  // 2nd cyllinder
5 50 250   // 3rd cyllinder
1 45 130   // 4th cyllinder
4 20 119   // 5th cyllinder

此处的输出应如下所示:
249  //the tot weight
1 2  //cyllinders which were chosen (in this case 1st and 2nd cyllinder)

我能找到249,所以重量很重,但我正在努力了解如何获取气缸索引,有人能给我一个提示或指导我如何实现它。这是计算权重的函数:
int ox,ni,n;
int o[1000],nit[1000],w[1000];

int best[22][80],next[22][80];

int solve()
    {
        memset(best, 0x3f, sizeof(best));
        best[0][0] = 0;
        for (int k = 0; k < n ;k++)
        {
           memcpy(next,best,sizeof(best));

           for (int i = 0; i <= ox ;i++)
           {
                for (int j = 0 ; j <= ni ;j++)
                {
                    next[min(ox,i+o[k])][min(ni,j+nit[k])]= min(best[i][j]+w[k], next[min(ox,i+o[k])][min(ni,j+nit[k])]);
                }
           }
           memcpy(best,next,sizeof(best));
        }
        cout << endl;

        return best[ox][ni];
    }

我试图在第三循环中制作这样的if语句:
if (((next[min(ox,i+o[k])][min(ni,j+nit[k])]) == (best[i][j]+w[k])) && ((min(ox, i+o[k]) == ox) || (min(ni, j+nit[k])== ni) )) 
                    {
                        cout << k << " ";
                    }

但这在大多数情况下都不起作用。谁能给我一个提示或指示我如何使该语句捕捉并打印正确的圆柱索引?

新的更新更改:
    #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <iostream>

using namespace std;

typedef struct {    /* typedef for struct containing tank vals */
    int ox,
        nit,
        wt;
} tank_t;

int main() {

    int ndivers = 0,        /* number of divers */
    oxreq = 0,          /* minimum oxygen required */
    nitreq = 0,         /* minimum nitrogen required */
    n = 0,              /* number of cylinders actually read */
    ncyl = 0,           /* number of cylinders from input file */
    wtmin = INT_MAX,    /* minimum weight (initialize to INT_MAX) */
    *indexes = NULL;    /* pointer to track tank indexes */
    tank_t *tanks = NULL, best;   /* pointer to tanks struct */

    best.ox = 0;

    /* allocate/validate storage for ncyl integers */
    if ((indexes = (int*)calloc(ncyl, sizeof *indexes)) == NULL) {
        perror("calloc-indexes");
        return 1;
    }

    /* allocate/validate storage for ncyl tanks */
    if ((tanks = (tank_t*)calloc(ncyl, sizeof *tanks)) == NULL) {
        perror("calloc-tanks");
        return 1;
    }

    cin >> ndivers;

    for(int i=0; i<ndivers; i++)
    {
        cin >> oxreq >> nitreq;
        cin >> ncyl;
        n = ncyl;

        for (int i = 0; i < ncyl; i++)
        {
            cin >> tanks[i].ox >> tanks[i].nit >> tanks[i].wt;
        }
    }



    /* loop over each tank to use as beginning in calc */
    for (int i = 0; i < n; i++) {
        int j = i + 1,      /* set 2nd index as next tank */
            *idx =(int*) calloc(n, sizeof *idx); /* allocate/zero temp index */
                                           /* can move idx alloc out of loop & memset here */
        if (!idx) { /* validate allocation */
            perror("calloc-idx");
            return 1;
        }
        /* use a temp tank_t struct tmp to accumulate values */
        tank_t tmp = { tanks[i].ox, tanks[i].nit, tanks[i].wt };
        idx[i] = 1;                     /* set 1st index value in tmp index */
        while (j < n) {                 /* loop over remaining tanks */
        idx[j] = 1;                 /* set next index as used */
        tmp.ox += tanks[j].ox;      /* add next tank ox */
        tmp.nit += tanks[j].nit;    /* add next tank nit */
        tmp.wt += tanks[j].wt;      /* add next tank wt */
                                        /* check if total ox & nit meet min, & wt < current min */
            if (tmp.ox > oxreq && tmp.nit > nitreq && tmp.wt < wtmin) {
                best = tmp;             /* save ox, nit & wt in best */
                wtmin = tmp.wt;         /* update minimum wt */
                memcpy(indexes, idx, n * sizeof *idx); /* copy to indexes */
                memset(idx, 0, sizeof *idx * n);   /* re-zero idx */
                memset(&tmp, 0, sizeof tmp);   /* zero tmp tank */
                idx[i] = 1;                     /* set 1st tank index */
                tmp.ox = tanks[i].ox;   /* set 1st tank values */
                tmp.nit = tanks[i].nit;
                tmp.wt = tanks[i].wt;
            }
            j++;    /* increment 2nd tank counter */
        }
        free(idx); /* free temp index */
    }
    free(tanks);   /* free tanks data - done with it */

    cout << best.wt;

    for (int i = 0; i < n; i++)
    {
        if (indexes[i])
            cout << i + 1 << " ";
    }

    free(indexes); /* free final indexes */

    return 0;
}

最佳答案

我不确定您使用struct来协调坦克信息的不同部分,而不是尝试从三个单独的数组协调索引的建议,您取得了多少进展。这里有一些想法和示例。该示例只是多种解决方案中的一种,并且是我的第一个切入点,即对 jar 信息进行求和以达到所需的最小oxnit,同时最大程度地降低总重量。

如果储 jar 重量的替代物碰巧提供了相同的最小重量,则不会尝试使oxnit最大化(这是您应该做的)

首先,如评论中所述,只要您需要在一条数据单元上协调多条信息,就需要考虑struct。在您的代码中,您尝试使用以下方法管理数据:

int ox,ni,n;
int o[1000],nit[1000],w[1000];

int best[22][80],next[22][80];

尽管可行,但为应该“动态”处理数据的项目分配固定存储空间似乎在单词“Go”中是错误的(更不用说混淆了)。相反,请为tank_type数据使用简单的struct,例如
typedef struct {    /* typedef for struct containing tank vals */
    int ox,
        nit,
        wt;
} tank_t;

(并且您可以使用typedef来避免始终写stuct ...)

好处是您现在可以为结构的no. of cylinders分配存储空间,然后使用所有储 jar 值的单坐标索引访问数据,例如
    tank_t *tanks = NULL, ...;   /* pointer to tanks struct */
    ...
    /* validate number of cylinders read from file */
    if (fscanf (fp, "%d", &ncyl) != 1 || ncyl < 1) {
        fprintf (stderr, "error: read failed - no. of cylinders.\n");
        return 1;
    }
    ...
    /* allocate/validate storage for ncyl tanks */
    if ((tanks = calloc (ncyl, sizeof *tanks)) == NULL) {
        perror ("calloc-tanks");
        return 1;
    }

现在,您只需将数据读入tanks[0].ox, tanks[0].nit, ... tanks[1].ox, tanks[1].nit, ...即可。

要跟踪您的坦克索引,只需分配no. of cylinders int即可,您可以将要与1进行比较的当前索引设置为剩余的剩余部分作为0。 (您可以将类似的临时索引用于工作目的,并将满足条件的当前最小储 jar 组合索引分配给将保留迭代结果的最终内存块)

问题的其余部分只是简单地提出逻辑,以遍历所有储 jar 组合,节省符合oxnit标准的每种组合的重量,并将每种组合与当前的最小数量进行比较。您只需要确保在实现逻辑时就可以根据需要重置值,从而获得有效的比较。

(首先想到的是类似于一组简单的数组排序例程的嵌套循环集,该例程将迭代并加在一起并比较各种组合。[毫无疑问,这是一种更有效的方法])

以下是结果,以作为一种采用方法的示例(内嵌其他注释以帮助您遵循)。还要注意,我只是在发布数据文件(包括注释)时对其进行读取,因此,如果实际文件忽略了注释,则可以(略微)简化读取例程。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

typedef struct {    /* typedef for struct containing tank vals */
    int ox,
        nit,
        wt;
} tank_t;

void empty_line (FILE *fp)  /* simple function to strip comments from */
{                           /* your input file */
    int c = fgetc (fp);

    while (c != '\n' && c != EOF)
        c = fgetc (fp);
}

int main (int argc, char **argv) {

    int ndivers = 0,        /* number of divers */
        oxreq = 0,          /* minimum oxygen required */
        nitreq = 0,         /* minimum nitrogen required */
        n = 0,              /* number of cylinders actually read */
        ncyl = 0,           /* number of cylinders from input file */
        wtmin = INT_MAX,    /* minimum weight (initialize to INT_MAX) */
        *indexes = NULL;    /* pointer to track tank indexes */
    tank_t *tanks = NULL, best = { .ox = 0 };   /* pointer to tanks struct */
    /* open file given as 1st argument or read from stdin */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    /* validate number of divers read */
    if (fscanf (fp, "%d", &ndivers) != 1 || ndivers < 1) {
        fprintf (stderr, "error: read failed - number of divers.\n");
        return 1;
    }
    empty_line (fp);    /* strip remaining comments in line */

    /* validate required ox and nit read from file */
    if (fscanf (fp, "%d %d", &oxreq, &nitreq) != 2 || 
                oxreq < 1 || nitreq < 1) {
        fprintf (stderr, "error: read failed - ox, nit required.\n");
        return 1;
    }
    empty_line (fp);

    /* validate number of cylinders read from file */
    if (fscanf (fp, "%d", &ncyl) != 1 || ncyl < 1) {
        fprintf (stderr, "error: read failed - no. of cylinders.\n");
        return 1;
    }
    empty_line (fp);

    /* allocate/validate storage for ncyl integers */
    if ((indexes = calloc (ncyl, sizeof *indexes)) == NULL) {
        perror ("calloc-indexes");
        return 1;
    }

    /* allocate/validate storage for ncyl tanks */
    if ((tanks = calloc (ncyl, sizeof *tanks)) == NULL) {
        perror ("calloc-tanks");
        return 1;
    }

    /* read/validate tank information - store in tanks */
    while (n < ncyl && fscanf (fp, "%d %d %d", &tanks[n].ox, 
            &tanks[n].nit, &tanks[n].wt) == 3) {
        empty_line (fp);
        n++;
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    /* loop over each tank to use as beginning in calc */
    for (int i = 0; i < n; i++) {
        int j = i + 1,      /* set 2nd index as next tank */
            *idx = calloc (n, sizeof *idx); /* allocate/zero temp index */
                            /* can move idx alloc out of loop & memset here */
        if (!idx) { /* validate allocation */
            perror ("calloc-idx");
            return 1;
        }
        /* use a temp tank_t struct tmp to accumulate values */
        tank_t tmp = { tanks[i].ox, tanks[i].nit, tanks[i].wt };
        idx[i] = 1;                     /* set 1st index value in tmp index */
        while (j < n) {                 /* loop over remaining tanks */
            idx[j] = 1;                 /* set next index as used */
            tmp.ox += tanks[j].ox;      /* add next tank ox */
            tmp.nit += tanks[j].nit;    /* add next tank nit */
            tmp.wt += tanks[j].wt;      /* add next tank wt */
            /* check if total ox & nit meet min, & wt < current min */
            if (tmp.ox > oxreq && tmp.nit > nitreq && tmp.wt < wtmin) {
                best = tmp;             /* save ox, nit & wt in best */
                wtmin = tmp.wt;         /* update minimum wt */
                memcpy (indexes, idx, n * sizeof *idx); /* copy to indexes */
                memset (idx, 0, sizeof *idx * n);   /* re-zero idx */
                memset (&tmp, 0, sizeof tmp);   /* zero tmp tank */
                idx[i] = 1;                     /* set 1st tank index */
                tmp.ox = tanks[i].ox;   /* set 1st tank values */
                tmp.nit = tanks[i].nit;
                tmp.wt = tanks[i].wt;
            }
            j++;    /* increment 2nd tank counter */
        }
        free (idx); /* free temp index */
    }
    free (tanks);   /* free tanks data - done with it */

    /* output results */
    printf ("best tank combo that meets: O2 >= %d, N >= %d\n\n"
            "  O2: %d\n  N : %d\n  wt: %d\n\n  tanks:",
            oxreq, nitreq, best.ox, best.nit, best.wt);
    for (int i = 0; i < n; i++)
        if (indexes[i])
            printf (" %d", i + 1);
    putchar ('\n');

    free (indexes); /* free final indexes */

    return 0;
}

示例输入文件
$ cat dat/tank.txt
1          //the number of divers
5 60       // ox=5 and ni=60 , the amonut of OX and NI the diver needs
5          //the number of cylinders to choose - n.
3 36 120   // 1st cyllinder => ox=3 / nit=36 / weight = 120
10 25 129  // 2nd cyllinder
5 50 250   // 3rd cyllinder
1 45 130   // 4th cyllinder
4 20 119   // 5th cyllinder

示例使用/输出
$ ./bin/tankopt <dat/tank.txt
best tank combo that meets: O2 >= 5, N >= 60

  O2: 13
  N : 61
  wt: 249

  tanks: 1 2

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何已分配的内存块,您都有两个责任:(1)始终保留指向该内存块的起始地址的指针,因此,(2)在没有内存块时可以将其释放需要更长的时间。

必须使用内存错误检查程序来确保您不尝试访问内存或不在分配的块的边界之外/之外写入,尝试读取或基于未初始化的值进行条件跳转,最后确定您可以释放已分配的所有内存。

对于Linux,valgrind是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。
$ valgrind ./bin/tankopt <dat/tank.txt
==6126== Memcheck, a memory error detector
==6126== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6126== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==6126== Command: ./bin/tankopt
==6126==
best tank combo that meets: O2 >= 5, N >= 60

  O2: 13
  N : 61
  wt: 249

  tanks: 1 2
==6126==
==6126== HEAP SUMMARY:
==6126==     in use at exit: 0 bytes in 0 blocks
==6126==   total heap usage: 7 allocs, 7 frees, 180 bytes allocated
==6126==
==6126== All heap blocks were freed -- no leaks are possible
==6126==
==6126== For counts of detected and suppressed errors, rerun with: -v
==6126== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认已释放已分配的所有内存,并且没有内存错误。

如果您还有其他问题,请告诉我。如前所述,该示例提供了一种用于迭代和索引跟踪的方法的示例-并非旨在(但最有效)

添加提示并从stdin读取

如果您仔细注意,如果文件名是第一个参数,则已经将代码设置为从文件中读取;如果没有参数,则从stdin读取。这可以通过在FILE声明中使用三元运算符来实现,例如:
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

三元运算符只是结构为if-else的简写(condition) ? value if true : value if false;。因此,在(argc > 1) ? fopen (argv[1], "r") : stdin;上方检查的是argc > 1,如果是,则打开argv[i]中给定的文件名,否则它只是将stdin分配给fp

知道您已经可以从stdin中读取(并按照上面的代码注释中所述将idx的分配移到循环外),您可以执行类似以下操作的提示输入:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

typedef struct {    /* typedef for struct containing tank vals */
    int ox,
        nit,
        wt;
} tank_t;

void empty_line (FILE *fp)  /* simple function to strip comments from */
{                           /* your input file */
    int c = fgetc (fp);

    while (c != '\n' && c != EOF)
        c = fgetc (fp);
}

int main (int argc, char **argv) {

    int ndivers = 0,        /* number of divers */
        oxreq = 0,          /* minimum oxygen required */
        nitreq = 0,         /* minimum nitrogen required */
        n = 0,              /* number of cylinders actually read */
        ncyl = 0,           /* number of cylinders from input file */
        wtmin = INT_MAX,    /* minimum weight (initialize to INT_MAX) */
        *indexes = NULL,    /* pointer to track tank indexes */
        *idx = NULL;        /* pointer to temp index */
    tank_t *tanks = NULL, best = { .ox = 0 };   /* pointer to tanks struct */
    /* open file given as 1st argument or read from stdin */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    if (fp == stdin)    /* prompt for no. of divers */
        fputs ("enter number of divers: ", stdout);

    /* validate number of divers read */
    if (fscanf (fp, "%d", &ndivers) != 1 || ndivers < 1) {
        fprintf (stderr, "error: read failed - number of divers.\n");
        return 1;
    }
    empty_line (fp);    /* strip remaining comments in line */

    if (fp == stdin)    /* prompt for no. of divers */
        fputs ("enter required oxygen and nitrogen: ", stdout);

    /* validate required ox and nit read from file */
    if (fscanf (fp, "%d %d", &oxreq, &nitreq) != 2 || 
                oxreq < 1 || nitreq < 1) {
        fprintf (stderr, "error: read failed - ox, nit required.\n");
        return 1;
    }
    empty_line (fp);

    if (fp == stdin)    /* prompt for no. of divers */
        fputs ("enter number of cylinders: ", stdout);

    /* validate number of cylinders read from file */
    if (fscanf (fp, "%d", &ncyl) != 1 || ncyl < 1) {
        fprintf (stderr, "error: read failed - no. of cylinders.\n");
        return 1;
    }
    empty_line (fp);

    /* allocate/validate storage for ncyl integers */
    if ((indexes = calloc (ncyl, sizeof *indexes)) == NULL) {
        perror ("calloc-indexes");
        return 1;
    }

    /* allocate/validate storage for ncyl tanks */
    if ((tanks = calloc (ncyl, sizeof *tanks)) == NULL) {
        perror ("calloc-tanks");
        return 1;
    }

    if (fp == stdin)    /* prompt for no. of divers */
        fprintf (stdout, "enter cylinder info (ox, nit, wt.) per-line:\n\n"
                "  enter info for cylinder[%2d]: ", n + 1);

    /* read/validate tank information - store in tanks */
    while (fscanf (fp, "%d %d %d", &tanks[n].ox, 
            &tanks[n].nit, &tanks[n].wt) == 3) {
        empty_line (fp);
        if (++n == ncyl)
            break;
        if (fp == stdin)    /* prompt for no. of divers */
            fprintf (stdout, "  enter info for cylinder[%2d]: ", n + 1);
        }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    /* allocate/validate storage for temp indexes (idx) */
    if ((idx = calloc (n, sizeof *idx)) == NULL) {
        perror ("calloc-idx");
        return 1;
    }

    /* loop over each tank to use as beginning in calc */
    for (int i = 0; i < n; i++) {
        int j = i + 1;      /* set 2nd index as next tank */
        memset (idx, 0, sizeof *idx * n);   /* zero working index */
        /* use a temp tank_t struct tmp to accumulate values */
        tank_t tmp = { tanks[i].ox, tanks[i].nit, tanks[i].wt };
        idx[i] = 1;                     /* set 1st index value in tmp index */
        while (j < n) {                 /* loop over remaining tanks */
            idx[j] = 1;                 /* set next index as used */
            tmp.ox += tanks[j].ox;      /* add next tank ox */
            tmp.nit += tanks[j].nit;    /* add next tank nit */
            tmp.wt += tanks[j].wt;      /* add next tank wt */
            /* check if total ox & nit meet min, & wt < current min */
            if (tmp.ox > oxreq && tmp.nit > nitreq && tmp.wt < wtmin) {
                best = tmp;             /* save ox, nit & wt in best */
                wtmin = tmp.wt;         /* update minimum wt */
                memcpy (indexes, idx, n * sizeof *idx); /* copy to indexes */
                memset (idx, 0, sizeof *idx * n);   /* re-zero idx */
                memset (&tmp, 0, sizeof tmp);   /* zero tmp tank */
                idx[i] = 1;                     /* set 1st tank index */
                tmp.ox = tanks[i].ox;   /* set 1st tank values */
                tmp.nit = tanks[i].nit;
                tmp.wt = tanks[i].wt;
            }
            j++;    /* increment 2nd tank counter */
        }
    }
    free (idx); /* free temp index */
    free (tanks);   /* free tanks data - done with it */

    /* output results */
    printf ("\nbest tank combo that meets: O2 >= %d, N >= %d\n\n"
            "  O2: %d\n  N : %d\n  wt: %d\n\n  tanks:",
            oxreq, nitreq, best.ox, best.nit, best.wt);
    for (int i = 0; i < n; i++)
        if (indexes[i])
            printf (" %d", i + 1);
    putchar ('\n');

    free (indexes); /* free final indexes */

    return 0;
}

示例使用/输出(从带有提示的stdin中读取)
$ ./bin/tankopt2
enter number of divers: 1
enter required oxygen and nitrogen: 5 60
enter number of cylinders: 5
enter cylinder info (ox, nit, wt.) per-line:

  enter info for cylinder[ 1]: 3 36 120
  enter info for cylinder[ 2]: 10 25 129
  enter info for cylinder[ 3]: 5 50 250
  enter info for cylinder[ 4]: 1 45 130
  enter info for cylinder[ 5]: 4 20 119

best tank combo that meets: O2 >= 5, N >= 60

  O2: 13
  N : 61
  wt: 249

  tanks: 1 2

关于c++ - ScubaDiv编程,输出时出现逻辑错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50165900/

相关文章:

algorithm - 找到一系列间隔的最有效分组

c++ - 查找坐标中值以构建 kd 树(2D)-C++

c++ - 在工作目录之外的 C++ 中打开文件

python - 解析 "ordered"列表并找到相应的和和加数的算法?

python - 学习算法

c++ - 用 2x1 block 平铺 2xM 阵列以最大化差异 - INOI 2008,P2

arrays - 给定一个包含 n 个整数的列表,找到大于 X 的最小子集和

c++ - 将抽象类传递给 i/o 宏

c++ #define一个带括号的宏?

c# - 改变循环结构中生成的对象的旋转 - Unity C#