zoukankan      html  css  js  c++  java
  • POJ1837 Balance 背包

    题目大意: 有一个天平,天平左右两边各有若干个钩子,总共有C个钩子(每个钩子有相对于中心的距离,左负右正),有G个钩码,求将钩码全部挂到钩子上使天平平衡的方法的总数。

    将每个砝码看作一组,组内各个物品的体积为每个挂钩与该砝码形成的力矩,背包总体积严格为0,这便是分组背包计数问题(特殊点:每一组必须出一个物品,而不是至多出一个物品)。由于c++不允许负的数组下标,所以每次更新时,j要加上offsetJ。

    实现分组背包计数问题时,可以用填表法(找以前节点求自己值)(DP1)或刷表法(找以后节点更新以后值)(DP2)。由于刷表法时,如果DP[i][j]==0,可以跳过,所以节省时间。

    注意:

    • 不可以用一维数组倒序循环来表示DP数组,因为j-objV可能比j还大
    • 同理,每次判断时,不是j-objV>=0,0代表天平的支点而不是左端点。所以应当为j-objV>=minJ。
    #include <cstdio>
    #include <cstring>
    #include <cstdarg>
    using namespace std;
    
    const int MAX_V = 16000, MAX_OBJ = 30, MAX_HOOP = 30;
    const int Plus = 7500;
    #define Sub(x) x+offsetJ
    int TotHoop;
    int Len[MAX_HOOP], W[MAX_OBJ];
    
    void _printf(char *format, ...)
    {
    #ifdef _DEBUG
        va_list(args);
        va_start(args, format);
        vprintf(format, args);
        va_end(args);
    #endif
    }
    
    int DP1(int totHoop, int totDev, int *_w, int *_len)
    {
        int minJ = 0, maxJ = 0, wSum = 0, offsetJ, objV = 0;
        static int DP[MAX_OBJ][MAX_V];
        for (int dev = 1; dev <= totDev; dev++)
            wSum += _w[dev];
        for (int hoop = 1; hoop <= totHoop; hoop++)
            _len[hoop] > 0 ? maxJ += _len[hoop] * wSum : minJ += _len[hoop] * wSum;
        offsetJ = -minJ;
        DP[0][offsetJ] = 1;
        _printf("+ offsetJ %d maxJ %d
    ", +offsetJ, maxJ);
        for (int i = 1; i <= totDev; i++)
            for (int j = minJ; j <= maxJ; j++)
                for (int hoop = 1; hoop <= totHoop; hoop++)
                {
                    objV = _w[i] * _len[hoop];
                    if (j - objV >= minJ && j - objV <= maxJ)
                    {
                        DP[i][j + offsetJ] += DP[(i - 1)][j + offsetJ - objV];
                        _printf("%d=DP[%d][%d] += DP[%d][%d]=%d
    ", DP[i][j + offsetJ], i, j, i - 1, j - _w[i] * _len[hoop], DP[i - 1][j - _w[i] * _len[hoop] + offsetJ]);
                    }
                }
        return DP[totDev][offsetJ];
    }
    
    int DP2(int totHoop, int totDev, int *_w, int *_len)
    {
        int minJ = 0, maxJ = 0, wSum = 0, offsetJ, objV = 0;
        static int DP[MAX_OBJ][MAX_V];
        for (int dev = 1; dev <= totDev; dev++)
            wSum += _w[dev];
        for (int hoop = 1; hoop <= totHoop; hoop++)
            _len[hoop] > 0 ? maxJ += _len[hoop] * wSum : minJ += _len[hoop] * wSum;
        offsetJ = -minJ;
        DP[0][offsetJ] = 1;
        _printf("+ offsetJ %d maxJ %d
    ", + offsetJ, maxJ);
        for (int i = 0; i < totDev; i++)
            for (int j = minJ; j <= maxJ; j++)
                if (DP[i][j+ offsetJ])
                    for (int hoop = 1; hoop <= totHoop; hoop++)
                    {
                        objV = _w[i+1] * _len[hoop];
                        DP[i + 1][j + objV + offsetJ] += DP[i][j + offsetJ];
                        _printf("%d=DP[%d][%d] += DP[%d][%d]=%d
    ", DP[i+1][j+objV+ offsetJ], i+1, j+objV+ offsetJ, i, j+ offsetJ , DP[i][j + offsetJ]);
                    }
        return DP[totDev][offsetJ];
    }
    
    int main()
    {
    #ifdef _DEBUG
        freopen("c:\noi\source\input.txt", "r", stdin);
    #endif
        int totDevice, vCnt = 0, totV = 0, maxV = 0, minV = 0;
        scanf("%d%d", &TotHoop, &totDevice);
        for (int i = 1; i <= TotHoop; i++)
            scanf("%d", i + Len);
        for (int i = 1; i <= totDevice; i++)
            scanf("%d", i + W);
        printf("%d
    ", DP2(TotHoop, totDevice, W, Len));
        return 0;
    }
    View Code
  • 相关阅读:
    编程注意事项(2010.12.25)
    单链表冒泡排序
    关于选书与读书
    记录一下后几年要看的书
    我的第一次面试经历
    使用IntelliJ IDEA配置Tomcat(入门)
    linux中Find命令的使用
    profile,bashrc,bash_profile之间的区别和联系
    Linux的启动流程、模块管理与boot Loader
    linux(Centos)下软件安装的方式总结
  • 原文地址:https://www.cnblogs.com/headboy2002/p/8511849.html
Copyright © 2011-2022 走看看