【nowcoder 110246】Dima and Salad

285 篇文章 3 订阅
23 篇文章 0 订阅

Dima and Salad

题目链接:nowcoder 110246

到牛客看:

——>点我跳转<——

题目大意

有一些东西,对于某个东西只能选或不选。每个东西有价值和消耗。

然后要你在保证总价值除以总消耗的值为给定的 k 的情况下,总价值尽可能高。
如果无法弄出,则输出 -1,否则输出最大总价值。

思路

这道题很明显就是 01 背包。

那我们先想想暴力的 01 背包是怎么弄的。
那就是开三维, f i , j , k f_{i,j,k} fi,j,k 表示到第 i i i 个东西,总价值是 j j j,消耗是 k k k,这样的状态是否存在。

那你这样空间滚动之后是可以,但是会 TLE。

那你考虑把性质相同的可以弄在一起的状态弄在一起,因为它只是要两个相除是一个数,总有一些状态是相同的。
那我们考虑怎么找。

就从公式找起。
在这里插入图片描述
a i , b i a_i,b_i ai,bi 分别表示你你选的物品的价值和消耗组成的数组。

那因为下面如果要一个一个来弄是不可以每次除,那我们移项,是它可以分步处理。
可以化出这个式子:
∑ j = 1 m a j − k × ∑ j = 1 m b j = 0 \sum\limits_{j=1}^{m}a_j-k\times \sum\limits_{j=1}^{m}b_j=0 j=1majk×j=1mbj=0
那很明显 ∑ j = 1 m \sum\limits_{j=1}^{m} j=1m 可以放到最外面。
∑ j = 1 m ( a j − k × b j ) = 0 \sum\limits_{j=1}^{m}(a_j-k\times b_j)=0 j=1m(ajk×bj)=0

那我们就可以把后面的两维弄成一维,用 a i − k × b j a_i-k\times b_j aik×bj 表示状态。

那原来只是表示这个状态是否存在,那现在我们改成这样,就要看一下题目的要求。题目要总价值尽可能大,那我们就表示的是这个状态能有的最大价值。

然后很明显,状态会有负数,那我们就给它第二维的状态都加一个值使得全都是非负数,就解决了这个问题。

可以用滚动数组来优化空间,但是不优化也可以过,我就懒得优化。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#define di 20000//因为状态有负数,所以要统一移一下

using namespace std;

int n, k, a[101], b[101];
int c[101], f[101][40001];

int main() {
	scanf("%d %d", &n, &k);
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
	for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
	
	for (int i = 1; i <= n; i++)//算出对应的状态值
		c[i] = a[i] - b[i] * k;
	
	memset(f, -1, sizeof(f));
	f[0][di] = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 40000; j >= 0; j--)
			if (f[i - 1][j] != -1) {
				//买
				if (j + c[i] <= 40000 && j + c[i] >= 0)
				 f[i][j + c[i]] = max(f[i][j + c[i]], f[i - 1][j] + a[i]);
				 //不买
				f[i][j] = max(f[i][j], f[i - 1][j]);
			}
	
	//记得这里第二维不是看 0,而是看移动之后的 “0” 对应的位置
	if (f[n][di]) printf("%d", f[n][di]);
		else printf("-1");
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值