<题目链接>
题目大意:
在一个水果篮里有n种水果,并且这些水果每一种都有一个美味度和一个卡路里的属性, 小明要从这些水果中选出来一些做一个水果沙拉, 并且要求他的水果沙拉的美味度是卡路里的k倍,问小明是否可以做出这么一个水果沙拉,若不能输出-1,否则输出复合要求的最大的美味值。
解题思路:
题目的限制条件为物品的价值总和与卡路里的比值要为K,这个控制,于是我们将卡路里总和乘到的右边,然后移项,可得(a1-k*b1)+(a2-k*b2)+……+(an-k*bn)=0。因此就将 (ai-k*bi)作为物品i的一个新的属性,就将本问题转化为了01背包问题, 将(ai-k*bi)看成物品的重量,ai为物品的价值,0为背包的总容量。但是如果这样的话,(ai-k*bi)会出现负数。
为了处理这个问题,有两种处理方案;
一:将等式两边同时+N,使得 (ai-k*bi)全部为正
二:用两个dp,正的跑一遍,负的跑一遍,然后在把它们相加就是答案,至于题目的限制条件,则可以通过取相同的 i 值来实现,因为dd[i]中的体积 i 为实际体积的相反数。
下面的是第二种方案:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std; const int maxn = ;
#define INF 0x3f3f3f3f
int dp[maxn], dd[maxn];
struct node
{
int val, k;
int cnt;
}; int main()
{
int n, m;
while (scanf("%d %d", &n, &m) != EOF)
{
node arr[];
for (int i = ; i <= n; i++)
scanf("%d", &arr[i].val);
for (int i = ; i <= n; i++)
{
scanf("%d", &arr[i].k);
arr[i].cnt = arr[i].val - m * arr[i].k;
} memset(dp, -INF, sizeof(dp)); //初始化为负无穷是为了能够使dp[j]表示恰好装满 i 体积的情况
memset(dd, -INF, sizeof(dd));
dp[] = dd[] = ;
for (int i = ; i <= n; i++)
{
if (arr[i].cnt >= )
{
for (int j = ; j >= arr[i].cnt; j--)
{
dp[j] = max(dp[j], dp[j - arr[i].cnt] + arr[i].val);
}
}
else
{
arr[i].cnt = -arr[i].cnt;
for (int j = ; j >= arr[i].cnt; j--)
{
dd[j] = max(dd[j], dd[j - arr[i].cnt] + arr[i].val);
}
}
} int ans = -0x3f;
for (int i = ; i <= ; i++)
{
if (dp[i] == && dd[i] == )continue; //因为ans初始化为-0x3f,所以要跳过dp[0]==dd[0]==0的情况
ans = max(ans, dd[i] + dp[i]); //因为dp[i],dd[i]表示恰好装满i容量的最大价值,所以,题目的限制条件:(ai-k*bi)的总和=0,就可以通过dd[i],dp[i]取相同的i值来实现
}
ans == -0x3f ? printf("-1\n") : printf("%d\n", ans); }
return ;
}
2018-07-28