题解 融合矿石

融合矿石

用到了一个不错的 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;
}

posted @ 2025-07-04 17:58  Yun_Mo_s5_013  阅读(8)  评论(0)    收藏  举报