#include <iostream>
#include <cstring>
#define INF 0x3f3f3f
using namespace std;
//目标:寻找背包容量与价值。
//已知:智商+幽默感(看似都是属性,咋整?) 但是0-1背包的特征很明显,对每头牛,取or不取,那每头牛肯定是一个价值量
//两个属性,一个做容量,另一个做价值。 最后加判断求和。
//https://blog.youkuaiyun.com/Tc_To_Top/article/details/49871783
const int mid = 100000;
const int N = 110;
int ts[N];
int tf[N];
int dp[mid*2+10];
int n;
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d %d",&ts[i],&tf[i]);
}
memset(dp,-INF,sizeof(dp));//因为有负数,所以初始化为一个很小的负数
dp[mid]=0;//起点为0
for(int i=0;i<n;i++){
if(ts[i]>0){
for(int j=mid*2-1;j>=ts[i];j--){//j>=ts[i]的意思是能放的下开ts[i]
//直接从mid*2-1开始遍历,怎么就说明挪了100000个位置呢?
// 联想一下最普通的0-1背包,那个是从容器最大值开始遍历,这个的最大值变成什么呢?变成200000(i是dp下标,表示的是容量,即ts的和,范围是-100000~100000)
dp[j]=max(dp[j],dp[j-ts[i]]+tf[i]);
}
}else{//ts[i]<0时正序遍历,因为j-ts[i]此时大于0了,从表格上来看,就是上面行的更新需要下面行的原始数据,所以正着遍历,后面的行还是原始数据没有变。
//https://blog.youkuaiyun.com/dr5459/article/details/9220563
//体积总和也是有上下限的,可以在上面输入的时候就求出来。
//一个0-1背包,一个完全背包???????????????????????????????????????????????????
//https://www.2cto.com/kf/201508/427415.html 对称的,正负在坐标轴上关于原点对称?
//int j = S[i]; j - S[i] < mid*2; ++j 为啥呢? j减去ts[i],剩下的范围小于总的,也就是ts[i]没把全部范围用掉?
//好像不是这么理解,
//其实用范围这一说可以理解,上面ts[i]为正时,是所有可能影响的都要包括,而这里可能影响的范围变了,因为ts[i]是负的,下面的减减相遇是加,上面范围自然就更小,是为s[i]空出来的。
for(int j=0;j<=mid*2+ts[i];j++){//为什么要+ts[i]???????????????????????????????????????????????????
//注意dp范围,所有可能影响的都要包括(https://www.cnblogs.com/kuangbin/archive/2012/09/14/2684929.html)
//可能受ts[i]影响的,
dp[j]=max(dp[j],dp[j-ts[i]]+tf[i]);
}
}
}
int ans=0;
for(int i=mid;i<mid*2;i++){//从mid开始 ,因为mid之前是负数,不考虑。
if(dp[i]>=0){//必须有这个判断,为什么?因为有的dp还是初始化的-inf
ans = max(ans,dp[i]+i-mid);//i-mid就是ts的原始值
}
}
printf("%d",ans);
return 0;
}