【POJ 1837】 Balance

【POJ 1837】 Balance

简单01背包问题 有个小技巧就是去除负下标 一维数组表示用上的砝码数 二维表示当前砝码数下重量 由数据可知 最大重量为25*20*15 = 7500 最小也就是-7500 但是负下标会越界 因此我们把他们统一增加7500 范围变成(-7500)0~15000(7500) 中点0变成7500

这样变成了一般01背包问题 枚举加砝码数量 dp最大方案数即可
加了个挫优化 提前算好当前给的数据最小最大重量 然后在这之间找即可 还要判断左右(负正)

代码如下:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define INF 0x3f3f3f3f

using namespace std;

int dp[21][15000];
int l[21],w[21];//离中点距离(左-右+) 砝码重量

int main()
{
    int c,g,i,j,k,mm,mx,ms,xs;
    scanf("%d %d",&c,&g);
    mm = 15000,mx = 0;//初始最大最小力矩
    ms = xs = 0;//初始最大最小重量
    for(i = 1; i <= c; ++i)
    {
        scanf("%d",&l[i]);
        mm = min(mm,l[i]);//最小力矩
        mx = max(mx,l[i]);//最大力矩
    }
    for(i = 1; i <= g; ++i)
    {
        scanf("%d",&w[i]);
        ms += mm*w[i];//最小力矩*重量 加和 = 最小重量
        xs += mx*w[i];//最大力矩*重量 加和 = 最大重量
    }
    ms += 7500;
    xs += 7500;//平移7500去负
    memset(dp,0,sizeof(dp));
    dp[0][7500] = 1;//初始无法码时中点为一种方案 其余不满足
    for(j = 1; j <= g; ++j)//枚举砝码
    {
        for(i = 1; i <= c; ++i)//枚举位置
        {
            if(l[i] >= 0)//中点右侧(位置为正)
            {
                for(k =  l[i]*w[j]+ms; k <= xs; ++k)
                {
                    dp[j][k] = dp[j-1][k-l[i]*w[j]]+dp[j][k];
                }
            }
            else//中点左侧(位置为负)
            {
                for(k = ms; k <= xs+l[i]*w[j]; ++k)
                {
                    dp[j][k] = dp[j-1][k-l[i]*w[j]]+dp[j][k];
                }
            }
        }
    }
    printf("%d\n",dp[g][7500]);
    return 0;
}

你可能感兴趣的:(二维01背包)