ZOJ 1013 Great Equipment (DP)

本文介绍了一种三维背包问题的经典动态规划解决方案。通过定义状态dp[n][x][y]表示前n辆车装入x个装备1和y个装备2后能容纳的最大装备3数量,并采用滚动数组优化空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      很巧妙很经典的一道DP, 其中dp[n][x][y] 表示前n辆车装了x个装备1和y个装备2之后能装的最多的装备3的个数

,而把第n辆车上装的ix,iy装备去掉后就是前n-1辆车在拿了x - ix,y - iy个1,2装备后所能装的最大的装备3的个数

所以 dp[n][x][y] = max(dp[n - 1][x -ix x'][y - iy y'] + iz);  其中即x'=x - ix, y'=y - iy, 对ix和iy进行遍历即可得到

dp[n][x][y]; 而ix和iy的范围为第n辆车可以装的x和y的装备个数

由于空间限制,用滚动数组,(N-1维数组记录一个N维组合,然后对其DP求得最后一个数的方法就很棒)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <fstream>

using namespace std;

const int MAX_CAP = 501;
const int MAX_CAR = 101;

int dp[2][MAX_CAP][MAX_CAP]; //for dp the number of x,y,z
int w[MAX_CAR]; //the weight that caravans can carry
int s[MAX_CAR]; //the size that caravans can carry

inline void getMax(int& a, int b)
{
    if(a < b)
        a = b;
}

int main(int argc, char *argv[])
{
   // ifstream cin("input.txt");

    int cas = 0; //for cout the cases

    //for cin the input
    int n;
    int w1, s1, d1;
    int w2, s2, d2;
    int w3, s3, d3;

    int c1, c2, c3, d4;

    while(cin>>n)
    {
        //the end of input
        if (n == 0)
        {
            return 0;
        }

        cin>>w1>>s1>>d1;
        cin>>w2>>s2>>d2;
        cin>>w3>>s3>>d3;

        cin>>c1>>c2>>c3>>d4;

        for (int i = 1; i <= n; ++i)
        {
            cin>>w[i]>>s[i];
        }

        //init the dp array
        memset(dp[0], 0, sizeof(dp[0]));

        int max_x = 0, max_y = 0; //store the max number of x,y so far
        int prev = 0, now = 1;
        int tmp, nx, ny, nz; // record the max ny

        for (int i = 1; i <= n; ++i)
        {
            nx = min(w[i]/w1, s[i]/s1); //the number of x
            memset(dp[now], -1, sizeof(dp[now]));

            for (int j = 0; j <= nx; ++j)
            {
                ny = min((w[i]- j * w1)/w2, (s[i] - j * s1)/s2);
                if (j == 0) 
                    tmp = ny;

                for(int k = 0; k <= ny; ++k)
                {
                    nz = min((w[i] - j * w1 - k * w2)/w3, (s[i] - j * s1 - k * s2)/s3);
                    for (int a = 0; a <= max_x; ++a)
                    {
                        for (int b = 0; b <= max_y; ++b)
                        {
                            if (dp[prev][a][b] != -1)
                            {
                                getMax(dp[now][a + j][b + k], dp[prev][a][b] + nz);
                            }
                        }
                    }
                }

                    
            }

            max_x += nx;
            max_y += tmp;
            swap(now, prev);
        }


        //print the result
        if (cas ++)
            cout<<endl;


        int rst = 0;
        int def = max(0, d4 - c1 * d1 - c2 * d2 - c3 * d3);

        for (int i = 0; i <= max_x; ++i)
        {
            for (int j = 0; j <= max_y; ++j)
            {
                if (dp[prev][i][j] != -1)
                {
                    int num = min(i/c1, j/c2);
                    num = min(num, dp[prev][i][j]/c3);
                    getMax(rst, i * d1 + j * d2 + d3 * dp[prev][i][j] + def * num);
                }
                
            }
        }

        cout<<"Case "<<cas<<": "<<rst<<endl;


    }



    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值