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;
}