Acwing 1020.潜水员 (二维约束的01背包)

传送门

潜水员为了潜水要使用特殊的装备。

他有一个带2种气体的气缸:一个为氧气,一个为氮气。

让潜水员下潜的深度需要各种数量的氧和氮。

潜水员有一定数量的气缸。

每个气缸都有重量和气体容量。

潜水员为了完成他的工作需要特定数量的氧和氮。

他完成工作所需气缸的总重的最低限度的是多少?

例如:潜水员有5个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:

3 36 120

10 25 129

5 50 250

1 45 130

4 20 119
如果潜水员需要5升的氧和60升的氮则总重最小为249(1,2或者4,5号气缸)。

你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。

输入格式
第一行有2个整数 m,n。它们表示氧,氮各自需要的量。

第二行为整数 k 表示气缸的个数。

此后的 k 行,每行包括ai,bi,ci,3个整数。这些各自是:第 i 个气缸里的氧和氮的容量及汽缸重量。

输出格式
仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的最低值。

数据范围
1≤m≤21,
1≤n≤79,
1≤k≤1000,
1≤ai≤21,
1≤bi≤79,
1≤ci≤800
输入样例:
5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
输出样例:
249

思路:设最终需要的氧气总量为m,氮气总量为w,物品个数为n,每个物品的容量、重量如题,用a,b,c数组表示。

正常情况下的01背包,是不超过背包容量的,而这里要求的是最终选出来物品的a[i]总值>=m,选出的物品b[i]总值>=w,而我们需要保证满足这两个条件的情况下让c[i]的总和最小。一开始没什么思路想着,把背包装满,枚举所有比m大,比w大的状态,代表装满背包需要的最小价值,取min,但是发现,数组都开不了那么大,只能放弃。那么就用到一个非常巧妙的思路转换。

如果按着将将背包装满的方式,dp[j][k]表示氧气为j,氮气为m的最小总重量。我们需要把数组初始化为inf,只有0,0这个状态为0,这样的话转移方程为
dp[j][k]=min(dp[j][k], dp[j-a[i]][k-b[i]]+c[i]) ,这样只有在j>=a[i]并且k>=b[i]的情况下才能转移。但是这个题来说,我们加上当前物品以后气体如果溢出背包容量,也没关系,说明我们当前的物品自己就可以满足题目要求,前面i-1个物品不需要贡献气体了。那么我们其实后面那个限制条件其实可以不要,如果j同理。

代码:

#include
#include
#include
using namespace std;
const int N = 1005;
#define inf 0x3f3f3f3f
int dp[30][105],a[N],b[N],c[N];
int main()
{
     
    int n,m,w;
    cin>>m>>w>>n;
    for(int i=0;i<n;i++)
        cin>>a[i]>>b[i]>>c[i];
    memset(dp,inf,sizeof dp);
    dp[0][0]=0;
    for(int i=0;i<n;i++)
        for(int j=m;j>=0;j--)
            for(int k=w;k>=0;k--)
            {
     
                int x=j-a[i],y=k-b[i];
                x=max(x,0);y=max(y,0);
                dp[j][k]=min(dp[j][k],dp[x][y]+c[i]);
            }
    cout<<dp[m][w]<<endl;
    return 0;
}

你可能感兴趣的:(背包,dp)