洛谷 P1060 开心的金明(背包思路)

https://www.luogu.com.cn/problem/P1060

题意:

有n元m件物品,每个物品v元、价值w,只能拿一次,问n元能拿取的最大价值

思路:

  • 选取方式:对于每件物品只有两种选择,选与不选。若选则减去这个物品价钱并这个物品的增加价值,即f[j-v]+w (j是此时的钱数,v、w是这个物品的价钱和价值,f[j]是j钱能拿取的价值)。若不选则无变化,跳到下一个物品。每个物品都进行这种选择。

  • 实现方式:很容易想到用递归来实现,每个物品选与不选标记一下,用dfs()递归下去,这是针对物品进行遍历的,条件是买不买当前的物品。

    我们也可以针对钱进行遍历,当钱充足时,每个物品都试着买一下,若能使价值变大就买,若不能就不买。若采取这种方式,会遇到一个问题,怎么才能遍历所有可能?若是物品都摆出来,我们拿着n元去试着买一次,这样肯定是错的,这样买不到后面的物品,钱会在前几个物品上花完。

    我们若想都试着买到每一个物品,可以换个思路,一次只摆一个物品,对于摆出的物品做出买与不买的决定
    为了使我们不会乱花钱,每次在进行选择的时候都把钱加满,即为初始钱数。
    贪心的我们总想把钱恰好花完,若恰好买了此次物品后没钱了之后就可以不用再选择了;若还有钱,而我们又不知道其它物品的价钱,难以推断此时的较好决定,聪明的我们可以采取暴力买东西(反正每次都会把钱补满),即n买一次、n-1买一次、n-2买一次…直到买不到为止,这样我们就可以把这个物品的决定和其它物品的选择联系起来。因为在其它物品选择的时候,当时的每一个钱数已经被我们试着买前面的物品了,f数组记录的是前面物品的最优解。
    最后一步,为了保证每个物品的每个钱数只能买一次,我们在钱数上应选取逆序遍历(试着模拟一下买法就可以得出了)。

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
struct node{
    
    
	int v,w;
};
node a[30];
int f[maxn];
int main()
{
    
    
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
    
    
		cin>>a[i].v>>a[i].w;
		a[i].w*=a[i].v;
	}
	for(int i=1;i<=m;i++)
	{
    
    
		for(int j=n;j>=0;j--)
		{
    
    
			if(j>=a[i].v)
			{
    
    
				f[j]=max(f[j],f[j-a[i].v]+a[i].w);
			}
		}
	}
	cout<<f[n];
	return 0;
 } 

猜你喜欢

转载自blog.csdn.net/xiaolan7777777/article/details/105641212