题目意思就是要求出smartness和funness之和的最大值,同时这两个分别也大于或等于零。
一个变形的01背包问题。这个题目纠结了很久,翻了挺多博客,看完都是一知半解,后来看到了一个代码很清晰的博客,才慢慢理解大概是怎么解的。
下面是我对这个代码的理解,可以结合代码来看一下。
先开一个很大的数组,一分为二,左边的一半存负数的情况,右边的一半存正数的情况,取中间值shift,假设其在这个“轴”上的坐标是0。这个数组是当s[i]为数组上的某个值时,f[i]该怎么取能使和最大。如果s[i]是负数的话,负数在数轴上是越往左边越小,所以应该从最大,也就是最右边开始往左推,此处最大应该是从0开始,(背包问题的常见思路,从最大的结果开始,检验这个结果能否成立),如果是正数则反过来,从最右边开始往左推,此处最大依据情况而定。这样遍历一次就能得到一个数组dp,这个dp的下标代表的是s[i],存的值就是f[i]的值。然后定义一个变量ans,初始化为0,,从0开始(s[i]>=0)遍历数组dp,如果对应存的值非负数(f[i]>=0),则ans取max(ans,i+dp[i+shift]),+shift从数轴的0开始往右遍历的意思。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=105;
const int maxs=200000;
const int shift=100000;
#define inf 100000000
int s[maxn],f[maxn],dp[maxs];
int main()
{
ios::sync_with_stdio(false);
int n,l,r,step,begin,end;
while(cin>>n)
{
for(int i=0;i<n;i++)
cin>>s[i]>>f[i];
for(int i=0;i<maxs;i++)
dp[i]=-inf;
dp[0+shift]=0;
l=r=0;
for(int i=0;i<n;i++)
{
l=min(l,l+s[i]);
r=max(r,r+s[i]);
step=1;
begin=l,end=r;
if(s[i]>0)
{
step=-1;
swap(begin,end);
}
for(int j=begin;j!=end+step;j+=step)
dp[j+shift]=max(dp[j+shift-s[i]]+f[i],dp[j+shift]);
}
int ans=0;
for(int i=0;i<=r;i++)
if(dp[i+shift]>=0)
ans=max(ans,i+dp[i+shift]);
cout<<ans<<endl;
}
return 0;
}