洛谷 P1757 通天之分组背包 题解
题目大意
给定 \(n\) 个物品,已知重量 \(a_i\)、价值 \(b_i\) 以及所述组数 \(c_i\),将它们装进可承受重量最大为 \(m\) 的背包,且每组至多只能放进 \(1\) 件物品。求背包中物体价值最大为多少。
思路分析
这是分组背包的模板题。
首先同 0-1 背包一样,我们需要遍历所有物品。我们可以先将同组的物品的下标装在同一个 vector 中。由于 \(c_i\) 为 int 范围,我们无法直接用它作为组数下标,考虑重新编号。之后,由于每组只能放进 \(1\) 件物品,我们状态转移方程需从上一组的结果转移过来。由于只用一维无法实现,考虑增加一维。定义 \(dp_{i,j}\) 表示前 \(i\) 组中背包容量为 \(j\) 时的最大价值。后面按 0-1 背包的滚动数组优化去写即可,但在前面应先将上一组的状态复制到本组,便于之后转移无需遍历前面所有组。
代码呈现
#include<bits/stdc++.h>
using namespace std;
typedef long long ll; // 十年 OI 一场空
const int N=1010,K=105;
int m,n,k;
int a[N],b[N],c[N];
ll dp[K][N];
map<int,int> mp;
vector<int> idx[K];
int main(){
scanf("%d%d",&m,&n);
for (int i=1;i<=n;++i) scanf("%d%d%d",a+i,b+i,c+i);
for (int i=1;i<=n;++i){
if (mp.count(c[i])) c[i]=mp[c[i]];
else c[i]=mp[c[i]]=++k; // 重新编号
idx[mp[c[i]]].push_back(i); // 存储下标
}
for (int t=1;t<=k;++t){
for (int i=1;i<=m;++i) dp[t][i]=dp[t-1][i]; // 注意
for (int i:idx[t]){
for (int j=m;j>=a[i];--j)
dp[t][j]=max(dp[t][j],dp[t-1][j-a[i]]+b[i]);
}
}
printf("%lld",dp[k][m]);
return 0;
}

浙公网安备 33010602011771号