#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

//01背包问题求解实现中最难最绕的地方:二维数组的思想用一维数组表达
//思考:dp[i][j]表示把 前i件 物品 放进容量j的背包里  i是下标(所以背包问题确实就是前面那些问题的推广) 
//这样就和前面的分析思想很类似了  
//其状态转移:
//情况1:i不放进背包 则dp[i][j] = dp[i-1][j] 
//情况2:i放进背包 则dp[i][j] = v[i] + dp[i][j-w[i]] 注意每件物品 既有价值,又有重量 
//取两个情况的最大值
//现在思考:如何把这个二维数组dp变为用一维数组表示? 
//事实上逻辑是这样的:因为目标是求dp[n][m],前面的所有行最后都是不需要的
//并且每一步只须用到相邻两行,而不需要“遍历”
//并且这相邻两行的操作“用左上方来更新右下方”的
//所以可以化为用一维表示!
//太清晰了xdm。 
//每波从右往左更新。
//盯着这个一维数组dp:更新了的部分是对应二维数组中第i行的数据   未更新的部分是对应二维数组中第i-1行的数据 
//然后整体规律上是:单次更新过程中:涉及的i-1行的数据在i行的数据的左侧 
//因此选择从右往左更新。 
//更新过程上,逻辑上还是要每行每个元素都更新 因为更新时每个元素都可能被用到 

const int MAXN = 100 + 10; //物品数目上限 
const int MAXM = 1000 + 10; //容量上限 

int dp[MAXM];//只保留了“容量”下标,而“件”的下标随着逻辑动态变化 
int v[MAXN];
int w[MAXN]; 

int main(){
	int m, n;//m是容量,n是物品数目
	while(scanf("%d%d", &m, &n) != EOF){
		memset(dp, 0, sizeof(dp));//“前0件物品”放入各种容量中的结果 
		for(int i=1; i<=n; i++){
			scanf("%d%d", &w[i], &v[i]);
		}
		for(int i=1; i<=n; i++){//虚空的“行” 但其实不虚空,因为w和v用到了i这个下标 
			for(int j=m; j>=w[i]; j--){//逻辑:如果当前选定的容量j连i都放不下的话  
										//那就是直接继承dp[i-1][j]
										//在这个一维表示法中表现出来就是  对dp[j]不作修改。 太帅了
				dp[j] = max(dp[j], dp[j-w[i]] + v[i]);//赋值号左边的dp默认行号i,右边的默认行号i-1 
			}
		}
		printf("%d\n", dp[m]);
	} 
	return 0;
}