题解 融合矿石
融合矿石
用到了一个不错的 Trick。
题目见这里。
观察数据范围,我们发现实际上背包的最大质量 \(m\) 并不是特别大。
每种矿石都有无限多,并且矿石之间可以融合。不妨先考虑对于每一种重量,最多能得到多少金辉石。
这个东西跑一边完全背包能够得到。
我们考虑第一遍背包得到的 \(f\) 数组的实质。实际上我们是给出了一种打包的方案,每一种重量相当于一个包,里面放了已经融合好的若干种不同矿石。
我们再考虑用这些融合矿石包填满我们计算价值的背包。矿石无限,每种重量的融合矿石我们也能无限制造。所以这又是一个完全背包问题。
我们把 \(i\) 当作物品重量,通过 \(f_i\) 计算物品价值,然后再跑一次完全背包就是答案。
题目不难,但是这种两次背包的思想值得参考。
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,T;
int v[11];
struct node{
int a,b;
}roc[5000],rck[5000];
int tot;
int f[5000],dp[5000];
int ans=-2147480000;
void update(){
for(int i=1;i<=n;i++){
roc[i].a=roc[i].b=0;
rck[i].a=rck[i].b=0;
}
for(int i=1;i<=m;i++){
f[i]=-1;
dp[i]=0;
}
memset(v,0,sizeof(v));
ans=-2147483647;
tot=0;
return;
}
signed main(){
cin>>T;
//dp数组初始赋值为-1,防止访问到无法转移的位置
memset(f,-1,sizeof(f));
while(T--){
for(int i=1;i<=10;i++){
cin>>v[i];
}
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>roc[i].a>>roc[i].b;
}
//第一次背包,dp计算每一可能的重量下最多能凑出多少金辉石
f[0]=0;
for(int i=1;i<=n;i++){
for(int j=roc[i].a;j<=m;j++){
if(f[j-roc[i].a]<=-1) continue;
f[j]=max(f[j],f[j-roc[i].a]+roc[i].b);
}
}
/*考虑到我们的矿石是无限的,我们可以把每一种重量理解为一种打包方式,
则现在我们希望知道如何安排我们打的包能够得到最大收益,所以我们把第一遍
dp得到的包作为新的物品,进行第二遍dp*/
for(int i=1;i<=m;i++){
if(f[i]<=-1) continue;
rck[++tot].a=i;
int w=v[(f[i]*10-1)/i+1]*i;
rck[tot].b=w;
}
for(int i=1;i<=tot;i++){
for(int j=rck[i].a;j<=m;j++){
dp[j]=max(dp[j],dp[j-rck[i].a]+rck[i].b);
}
}
cout<<dp[m]<<endl;
update();
}
return 0;
}

浙公网安备 33010602011771号