简体   繁体   中英

ScubaDiv programming, logical error with outputting

This problem is about a Diver or a number of divers which have to take cylinders which contain OXYGEN and NITROGEN inside, also a cylinder has its own weight. Using dynamic programming we have to come up with a solutions which tells the diver the best weight he can get with the desired Oxy and Nitro (to maximaze the time under water for the diver)

The input goes like this:

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

And the output here should look like:

249  //the tot weight
1 2  //cyllinders which were chosen (in this case 1st and 2nd cyllinder)

I can find the 249, so the weight but I am struggeling to understand how to get the indexes of the cyllinders, can anyone give me a hint or direct me how can I achive it. Here is the function which calculates the Weight:

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];
    }

I tried to make if statement like this in the 3rd for loop:

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 << " ";
                    }

But it is not working in most of the cases. Can anyone give me a hint or direct me how to make the statement to catch and print the correct cylinders indexes?


The new updated changes:

    #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;
}

I'm not sure how much progress you have made with the suggestion given to use a struct to coordinate the different pieces of tank information rather than trying to coordinate indexes from three separate arrays. Here are a few thoughts and an example. The example is just one of a myriad ways to approach this, and was just my first cut at summing the tank information to meet the minimum ox and nit required while minimizing the combined weight overall.

No attempt was made to maximize ox or nit available if tank weights alternatives happen to provide the same minimum weight (which is something you should do)

First, as addressed in the comment, anytime you have multiple pieces of information your need to coordinate at one unit of data, you need to be thinking struct . In your code you are attempting to manage your data with:

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

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

While doable, allocating fixed storage for a project that is supposed to handle data "dynamically" just seems wrong from the word "Go" (not to mention confusing). Instead, use a simple struct for your tank_type data, eg

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

(and you can use a typedef to avoid having to write stuct ... all the time)

The benefit is you can now allocate storage for no. of cylinders no. of cylinders of the structs and then access your data with a single-coordinated index for all tank values, eg

    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;
    }

Now you can simply read your data into tanks[0].ox, tanks[0].nit, ... tanks[1].ox, tanks[1].nit, ... .

To track your tank indexes, just allocate for no. of cylinders no. of cylinders int and you can set the current indexes you are comparing to 1 leaving the remainder as 0 . (you can use a similar temporary index for working purposes and assign the current minimum tank combination indexes that meet your criteria to the final block of memory that will hold the results of your iterations)

The remainder of the problem is simply coming up with logic to iterate over all tank combination, saving the weight of each combination that meets your ox and nit criteria, and comparing each such combination against the current minimum as you go. You just need to make sure in implementing your logic you are tracking are resetting values as required so you end up with valid comparison.

(the first thought that came to mind was a nested set of loops similar to a simple array-sort routine that will iterate over and add together and compare the various combinations. [there is no doubt a more efficient way to do it])

Below is the result, to serve as an example to one approach to take (with additional comments inline to help you follow along). Also note, I just read your data file as you had posted it (including the comments), so you can simplify your read routine (slightly) if your actual file omits the comments.

#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;
}

Example Input File

$ 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

Example Use/Output

$ ./bin/tankopt <dat/tank.txt
best tank combo that meets: O2 >= 5, N >= 60

  O2: 13
  N : 61
  wt: 249

  tanks: 1 2

Memory Use/Error Check

In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.

For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.

$ 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)

Always confirm that you have freed all memory you have allocated and that there are no memory errors.

Let me know if you have further questions. This example, as stated earlier, was to provide an example of one approach to take for the iterations and index tracking -- it is not intended to be to only way to do this (or the most efficient)


Adding Prompts and Reading from stdin

If you note carefully, the code is already setup to read from a file if the filename is given as the 1st argument, --or-- read from stdin if no argument is given. This is accomplished by use of a ternary operator in the FILE declaration, eg:

FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

A ternary operator is simply a short-hand if-else structured as (condition) ? value if true : value if false; (condition) ? value if true : value if false; . So above (argc > 1) ? fopen (argv[1], "r") : stdin; (argc > 1) ? fopen (argv[1], "r") : stdin; check is argc > 1 and if so opens the filename given in argv[i] , otherwise it just assigns stdin to fp .

Knowing that you can already read from stdin (and moving the allocation of idx outside the loop as mentioned in the code comments above), you could do something like the following to prompt for input:

#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;
}

Example Use/Output (reading from stdin w/prompts)

$ ./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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM