文章目录
采药(01背包)
✍
典型01背包模板题
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int N = (int) (2e5 + 10);
static int[][] dp = new int[110][1100]; //最长不上升子序列
static int k = 0, n = 0;
static int[] t = new int[N];
static int[] val = new int[N];
static int cnt = 0;
static int time = 0;
public static void main(String[] args) throws Exception{
String[] tm = br.readLine().split(" ");
time = Integer.parseInt(tm[0]);
n = Integer.parseInt(tm[1]);
for(int i = 1; i <= n; i++) {
String[] tv = br.readLine().split(" ");
t[i] = Integer.parseInt(tv[0]);
val[i] = Integer.parseInt(tv[1]);
}
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= time; j++) {
if(t[i] > j) {
dp[i][j] = dp[i - 1][j];
}else {
dp[i][j] = Math.max(dp[i - 1][j],dp[i - 1][j - t[i]] +val[i]);
}
}
}
System.out.println(dp[n][time]);
}
}
装箱问题(01背包)
✍
本题可以从两个方向去思考:
法一:
给定n个物品,及每个物品对应的体积,要让剩余空间最少,就是求放进去的体积的体积的最大值。我们将每个物品的体积当作背包的价值,不就等价于之前的求背包的最大价值了。
最后用总的体积-dp[n][vv]
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int[][] dp = new int[100][21000];
static int[] v = new int[21000];
static int n = 0,vv = 0;
static long ans = 0;
public static void main(String[] args) throws Exception{
vv = Integer.parseInt(br.readLine());
n = Integer.parseInt(br.readLine());
for(int i = 1; i <= n; i++) {
v[i] = Integer.parseInt(br.readLine());
}
//
// for(int j = 0; j <= vv; j++) {
// dp[0][j] = j;
// }
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= vv; j++) {
dp[i][j] = dp[i - 1][j];
if(j >= v[i]) {
dp[i][j] = Math.max(dp[i][j],dp[i - 1][j - v[i]] + v[i]);
}
}
}
System.out.println(vv - dp[n][vv]);
}
}
法2:
直接设dp[i][j]代表前i个物品,体积为j的最少剩余空间。
初始化dp[0][j]代表前0件物品,体积为j,那么它的最少剩余空间就是j,初始化这些状态。
然后进行转移
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int[][] dp = new int[100][21000];
static int[] v = new int[21000];
static int n = 0,vv = 0;
static long ans = 0;
public static void main(String[] args) throws Exception{
vv = Integer.parseInt(br.readLine());
n = Integer.parseInt(br.readLine());
for(int i = 1; i <= n; i++) {
v[i] = Integer.parseInt(br.readLine());
}
for(int j = 0; j <= vv; j++) {
dp[0][j] = j;
}
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= vv; j++) {
dp[i][j] = dp[i - 1][j];
if(j >= v[i]) {
dp[i][j] = Math.min(dp[i][j],dp[i - 1][j - v[i]]);
}
}
}
System.out.println(dp[n][vv]);
}
}
宠物小精灵之收服(二维费用01背包👍😘)
本题是01背包的拓展版-二维费用背包
与平时的01背包(一维费用背包的不同是多了一维代价)
一维费用背包我们一般只考虑背包体积是否放的下。
二维费用背包是:同时考虑了背包容积与背包承重。
针对于这个题目:
本题的两个花费分别是:
精灵球的数量和皮卡丘体力值
而获得的价值就是野生小精灵的数量,需要注意的是,匹拉丘的体力值不能为0,所以皮卡丘的体力值的范围为0-m-1,最后在找一个消耗体力值最小的即可。
需要注意的是,本题必须使用滚动数组优化掉一层,否则会MLE。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int[][] dp = new int[1100][510];
static int[] v = new int[110]; //伤害
static int[] w = new int[110]; //数量
static int n = 0,m = 0,k = 0,num = 0;
static long ans = 0;
//dp[i][j][k]只考虑前i个,精灵球数量不超过j,皮卡丘体力值不超过k的最大值
public static void main(String[] args) throws Exception{
String[] nmk = br.readLine().split(" ");
n = Integer.parseInt(nmk[0]); //精灵球数量
m = Integer.parseInt(nmk[1]); //皮卡丘体力值
num = Integer.parseInt(nmk[2]); //野生小精灵的数量
for(int i = 1; i <= num; i++) {
String[] wv = br.readLine().split(" ");
w[i] = Integer.parseInt(wv[0]);
v[i] = Integer.parseInt(wv[1]);
}
for(int i = 1; i <= num; i++) {
for(int j = n; j >= w[i]; j --) {
for(int k = m - 1; k >= v[i]; k--) {
dp[j][k] = Math.max(dp[j][k],dp[j - w[i]][k - v[i]] + 1);
}
}
}
int s = m;
while(s > 0 && dp[n][m-1] == dp[n][s - 1]) {
s--;
}
System.out.println(dp[n][m - 1] + " " + (m - s));
}
}
数字组合(01背包)
首相可以想到枚举所有数字的选不选的情况,然后判断是否可行,但是肯定是过不了的,时间复杂度太高了。
每个数字都有选不选,那不就和每个物品都可以放入或者不放入背包差不多嘛,所以可以采用01背包进行求解。
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int N = 110;
static int M = 11000;
static int[][] dp = new int[N][M];
//前i个数 构成m的方案数
static int n = 0, m = 0;
static int[] a= new int[N];
public static void main(String[] args) throws IOException {
String[] nm = br.readLine().split(" ");
n = Integer.parseInt(nm[0]);
m = Integer.parseInt(nm[1]);
String[] aa = br.readLine().split(" ");
for(int i = 1; i <= n; i++) {
a[i] = Integer.parseInt(aa[i - 1]);
}
dp[0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= m; j++) {
dp[i][j] += dp[i - 1][j];
if(a[i] <= j) {
dp[i][j] += dp[i - 1][j - a[i]];
}
}
}
System.out.println(dp[n][m]);
}
}
买书(完全背包)
✍
因为每本书可以买多本,不需要考虑书本数量的限制,我们只需要考虑钱就行,所以是完全背包。
❗:一定要注意v那一维一定要从0开始,要不会导致有些状态转换不过来。
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int M = 1100;
static int[] v = {0,10,20,50,100};
static int[][] dp = new int[5][M];
static int n = 0, m = 0;
public static void main(String[] args) throws IOException {
n = Integer.parseInt(br.readLine());
dp[0][0] = 1;
for(int i = 1; i <= 4; i++) { //书
for(int j = 0; j <= n; j++) { //money
for(int k = 0; k <= j / v[i]; k++) { //num
dp[i][j] += dp[i - 1][j - v[i]*k];
}
}
}
System.out.println(dp[4][n]);
}
}
优化版本完全背包:
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int M = 1100;
static int[] v = {0,10,20,50,100};
static int[] dp = new int[M];
static int n = 0, m = 0;
public static void main(String[] args) throws IOException {
n = Integer.parseInt(br.readLine());
dp[0] = 1;
for(int i = 1; i <= 4; i++) { //书
for(int j = v[i]; j <= n; j++) { //money
dp[j] += dp[j - v[i]];
}
}
System.out.println(dp[n]);
}
}
货币系统(完全背包)
和上一题基本相同,需要注意数据范围,我们需要开long
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int M = 3100;
static int[] v = new int[20];
static long[] dp = new long[M];
static int n = 0, m = 0;
public static void main(String[] args) throws IOException {
String[] nm = br.readLine().split(" ");
n = Integer.parseInt(nm[0]);
m = Integer.parseInt(nm[1]);
for(int i = 1; i <= n; i++) {
v[i] = Integer.parseInt(br.readLine());
}
dp[0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = v[i]; j <= m; j++) {
dp[j] += dp[j - v[i]];
}
}
System.out.println(dp[m]);
}
}
货币系统(🔺👍)
其实就是看哪些数可以被其他数凑出来,就可以去掉,看最后还剩下几个数即可。
首先可以想到的,肯定是小的可以凑出大的,大的不可以凑比自己还小的数。因此我们先给所有数排序,还需要注意多组样例之间的初始化问题,以及dp边界条件赋值。dp[i]表示是否可以被表示出来
对于每个可以凑出来的dp[j]我们可以使n–,如果不可以被凑出来,我们就看看能不能用它去凑其他更大的数。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Arrays;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int N = (int) (3e4 + 10);
static boolean[] dp = new boolean[N];
static int[] a = new int[N];
static int t = 0 , n = 0;
static int Inf = 0x3f3f3f3f;
public static void main(String[] args) throws IOException {
t = Integer.parseInt(br.readLine().trim());
while(t-- > 0) {
for(int i = 0; i < N; i++) {
dp[i] = false;
}
n = Integer.parseInt(br.readLine().trim());
String[] aa = br.readLine().split(" ");
for(int i = 1; i <= n; i++) {
a[i] = Integer.parseInt(aa[i - 1]);
}
Arrays.sort(a,1,1+n);
int ans = n;
dp[0] = true;
for(int i = 1; i <= n; i++) {
if(dp[a[i]]) {
ans--;
continue;
}
for(int j = a[i]; j <= a[n]; j++) {
dp[j] = dp[j] | dp[j-a[i]];
}
}
System.out.println(ans);
}
}
}
庆功会(多重背包)
和完全背包差不多,只不过这个有数量限制,只要理解了01背包,其实本质都是一样的。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int[] v = new int[510];
static int[] w = new int[510];
static int[] s = new int[510];
static long[][] dp = new long[510][6010];
static int n = 0, m = 0;
public static void main(String[] args) throws IOException {
String[] nm = br.readLine().split (" ");
n = Integer.parseInt(nm[0]);
m = Integer.parseInt(nm[1]);
for(int i = 1; i <= n; i++) {
String[] vws = br.readLine().split(" ");
v[i] = Integer.parseInt(vws[0]);
w[i] = Integer.parseInt(vws[1]);
s[i] = Integer.parseInt(vws[2]);
}
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= m; j++) {
// dp[i][j] = dp[i - 1][j];
for(int k = 0; k <= s[i]; k++) {
if(k*v[i] <= j) {
dp[i][j] = Math.max(dp[i][j],dp[i- 1][j - k*v[i]] + k * w[i]);
}
}
}
}
System.out.println(dp[n][m]);
}
}
混合背包问题(多重背包二进制优化为01背包👍⭐)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int[] v = new int[1100];
static int[] w = new int[1100];
static int[] s = new int[1100];
static long[][] dp = new long[1100][1100];
static int n = 0, vv = 0;
public static void main(String[] args) throws IOException {
String[] nm = br.readLine().split (" ");
n = Integer.parseInt(nm[0]);
vv = Integer.parseInt(nm[1]);
for(int i = 1; i <= n; i++) {
String[] vws = br.readLine().split(" ");
v[i] = Integer.parseInt(vws[0]);
w[i] = Integer.parseInt(vws[1]);
int num = Integer.parseInt(vws[2]);
if(num == -1) {
s[i] = 1;
}else if(num == 0) {
s[i] = vv/v[i];
}else {
s[i] = num;
}
}
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= vv; j++) {
for(int k = 0; k <= s[i]; k++) {
if(k*v[i] <= j) {
dp[i][j] = Math.max(dp[i][j],dp[i- 1][j - k*v[i]] + k * w[i]);
}
}
}
}
out.println(dp[n][vv]);
// out.flush();
}
}
上面的代码思路是正确的,但是会TLE,下面我们采用二进制优化将多重背包优化为多个01背包。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int[] v = new int[1100];
static int[] w = new int[1100];
static int[] s = new int[1100];
static long[] dp = new long[1100];
static int n = 0, vv = 0;
public static void main(String[] args) throws IOException {
String[] nm = br.readLine().split (" ");
n = Integer.parseInt(nm[0]);
vv = Integer.parseInt(nm[1]);
for(int i = 1; i <= n; i++) {
String[] vws = br.readLine().split(" ");
v[i] = Integer.parseInt(vws[0]);
w[i] = Integer.parseInt(vws[1]);
s[i] = Integer.parseInt(vws[2]);
}
for(int i = 1; i <= n; i++) {
if(s[i] == 0) {
//完全背包
for(int j = v[i]; j <= vv; j++) {
dp[j] = Math.max(dp[j], dp[j - v[i]] + w[i]);
}
}else {
//01背包和多重背包
if(s[i] == -1) {
//01背包
s[i] = 1;
}
//二进制优化
for(int k = 1; k <= s[i]; k *= 2) {
for(int j = vv; j >= k*v[i]; j--) {
dp[j] = Math.max(dp[j],dp[j - k*v[i]] + k*w[i]);
}
s[i] -= k;
}
if(s[i] != 0) {
for(int j = vv; j >= s[i]*v[i]; j--) {
dp[j] = Math.max(dp[j],dp[j - s[i]*v[i]] + s[i]*w[i]);
}
}
}
}
System.out.println(dp[vv]);
}
}
二维费用的背包问题(模板题🔥⭐)
同时限制体积和重量,和一维费用背包差不多。只是多了一个重量限制,在开一维就可以,然后我们使用滚动数组优化,注意是01背包,倒着遍历。
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int N = 1100;
static int[] v = new int[N];
static int[] w = new int[N];
static int[] m = new int[N];
static int[] s = new int[1100];
static long[][] dp = new long[110][110];
static int n = 0, vv = 0,mm = 0;
public static void main(String[] args) throws IOException {
String[] nvm = br.readLine().split(" ");
n = Integer.parseInt(nvm[0]);
vv = Integer.parseInt(nvm[1]);
mm = Integer.parseInt(nvm[2]);
for(int i = 1; i <= n; i++) {
String[] vmw = br.readLine().split(" ");
v[i] = Integer.parseInt(vmw[0]);
m[i] = Integer.parseInt(vmw[1]);
w[i] = Integer.parseInt(vmw[2]);
}
for(int i = 1; i <= n; i++) {
for(int j = vv; j >= v[i]; j--) {
for(int k = mm; k >= m[i]; k--){
dp[j][k] = Math.max(dp[j][k],dp[j-v[i]][k-m[i]] + w[i]);
}
}
}
System.out.println(dp[vv][mm]);
}
}
潜水员(二维费用的背包问题⭐👍)!
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int N = (int) (3e4 + 10);
static int[][][] dp = new int[1100][110][110];
static int[] v = new int[1100];
static int[] m = new int[1100];
static int[] w = new int[1100];
static int nn = 0 , vv = 0, mm = 0;
static int Inf = 0x3f3f3f3f;
public static void main(String[] args) throws IOException {
String[] nvm = br.readLine().split(" ");
nn = Integer.parseInt(nvm[0]);
vv = Integer.parseInt(nvm[1]);
mm = Integer.parseInt(nvm[2]);
for(int i = 1; i <= nn; i++) {
String[] vmw = br.readLine().split(" ");
v[i] = Integer.parseInt(vmw[0]);
m[i] = Integer.parseInt(vmw[1]);
w[i] = Integer.parseInt(vmw[2]);
}
// for(int i = 1; i <= nn; i++) {
// for(int j = vv; j >= v[i]; j--) {
// for(int k = mm; k >= m[i]; k--) {
// dp[i][j][k] = Math.max(Math.max(dp[i][j][k],dp[i - 1][j][k]),dp[i-1][j-v[i]][k-m[i]] + w[i]);
// }
// }
// }
for(int i = 1; i <= nn; i++) {
for(int j = 0; j <= vv; j++) {
for(int k = 0; k <= mm; k++) {
dp[i][j][k] = dp[i-1][j][k];
if(v[i] <= j && m[i] <= k) {
dp[i][j][k] = Math.max(dp[i][j][k],dp[i-1][j-v[i]][k-m[i]] + w[i]);
}
// dp[i][j][k] = Math.max(dp[i - 1][j][k],dp[i-1][j-v[i]][k-m[i]] + w[i]);
}
}
}
System.out.println(dp[nn][vv][mm]);
}
}
机器分配(分组背包)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int N = 110;
static int[][] a = new int[N][N];
static int[][] c = new int[N][N];
static long[][] dp = new long[N][N];
//前i个公司,前j个设备的盈利
static int n = 0, vv = 0,m = 0;
static int[] ans = new int[N];
public static void main(String[] args) throws IOException {
String[] nm = br.readLine().split(" ");
n = Integer.parseInt(nm[0]);
m = Integer.parseInt(nm[1]);
for(int i = 1; i <= n; i++) {
String[] aa = br.readLine().split(" ");
for(int j = 1; j <= m; j++) {
a[i][j] = Integer.parseInt(aa[j - 1]);
}
}
for(int i = 1; i <= n; i++) { //第i组
for(int j = 0; j <= m; j++) { //容量
for(int k = 0; k <= j; k++) { //第i组选第几个
if(dp[i][j] < dp[i - 1][j - k] + a[i][k]) {
dp[i][j] = dp[i-1][j-k] + a[i][k];
c[i][j] = j-k; //当前状态有i-1,j-k转换过来
}
}
}
}
int p = m;
int pos = n;
while(p != 0){
ans[pos] = p - c[pos][p];
p = c[pos][p];
pos --;
}
System.out.println(dp[n][m]);
for (int i = 1; i <= n; i ++) {
System.out.println(i + " " + ans[i]);
}
}
}
开心的金明(01背包)
✍
01背包裸题
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int N = 31000;
static long[] dp = new long[N];
static int[] lev = new int[N];
static long[] p = new long[N];
static int n = 0, vv = 0,m = 0;
public static void main(String[] args) throws IOException {
String[] nm = br.readLine().split(" ");
n = Integer.parseInt(nm[0]);
m = Integer.parseInt(nm[1]);
for(int i = 1; i <= m;i++) {
String[] pl = br.readLine().split(" ");
p[i] = Integer.parseInt(pl[0]);
lev[i] = Integer.parseInt(pl[1]);
}
for(int i = 1; i <= m; i++) {
for(int j = n; j >= p[i]; j--) {
dp[j] = Math.max(dp[j],dp[(int) (j - p[i])] + lev[i] * p[i]);
}
}
System.out.println(dp[n]);
}
}
背包问题求方案数(01背包求方案数😔❓)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int N = 1100;
static int mod = (int) (1e9 + 7);
static long[][] dp = new long[N][N];
static long[][] g = new long[N][N];
static int[] v = new int[N];
static int[] w = new int[N];
static int n = 0, vv = 0,m = 0;
static long ans = 0;
public static void main(String[] args) throws IOException {
String[] nv = br.readLine().split(" ");
n = Integer.parseInt(nv[0]);
vv = Integer.parseInt(nv[1]);
for(int i = 1; i <= n; i++) {
String[] vw = br.readLine().split(" ");
v[i] = Integer.parseInt(vw[0]);
w[i] = Integer.parseInt(vw[1]);
}
g[0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= vv; j++) {
dp[i][j] = dp[i - 1][j];
if(v[i] <= j) {
dp[i][j] = Math.max(dp[i][j],dp[i - 1][j - v[i]] + w[i]);
}
}
}
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= vv; j++) {
if (dp[i][j] == dp[i - 1][j])
g[i][j] = (g[i][j] + g[i - 1][j]) % mod;
if (j >= v[i] && dp[i][j] == dp[i - 1][j - v[i]] + w[i])
g[i][j] = (g[i][j] + g[i - 1][j - v[i]]) % mod;
}
}
for(int j = 0; j <= vv; j++ ) {
if(dp[n][j] == dp[n][vv]) {
ans = (ans + g[n][j]) % mod;
}
}
System.out.println(ans);
}
}
背包问题求具体方案(❓)
金明的预算方案(有依赖的背包👍⭐)
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
public class Main{
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
static int[][] dp = new int[100][32005];
static int[] v1 = new int[80];
static int[] p1 = new int[80];
static int[][] pri = new int[32005][5];
static int[][] w = new int[32005][5];
static int[] count = new int[80];
static int[] order = new int[80];
static int n = 0,m = 0,vv = 0;
static long ans = 0;
static int cnt = 0;
public static void main(String[] args) throws Exception{
String[] nm = br.readLine().split(" ");
n = Integer.parseInt(nm[0]);
m = Integer.parseInt(nm[1]);
for(int i = 1; i <= m; i++) {
String[] vpq = br.readLine().split(" ");
int vv = Integer.parseInt(vpq[0]);
int pp = Integer.parseInt(vpq[1]);
int qq = Integer.parseInt(vpq[2]);
if(qq != 0) {
int c = order[qq];
++count[c];
pri[c][count[c]] = vv;
w[c][count[c]] = pp;
}else {
++cnt;
v1[cnt] = vv;
p1[cnt] = pp;
order[i] = cnt;
}
}
// for(int i = 1; i <= cnt; i++) {
// System.out.println(count[i]);
// }
for(int i = 1; i <= cnt; i++) {
for(int j = 0; j <= n; j++) {
dp[i][j] = dp[i - 1][j]; //不选
if(j >= v1[i]) { //只选主件
dp[i][j] = Math.max(dp[i][j],dp[i - 1][j - v1[i]] + p1[i]*v1[i]);
}
if(j >= v1[i] + pri[i][1]) {
dp[i][j] = Math.max(dp[i][j],dp[i - 1][j - v1[i] - pri[i][1]] + p1[i] *v1[i] + pri[i][1] * w[i][1]);
}
if(j >= v1[i] + pri[i][2]) {
dp[i][j] = Math.max(dp[i][j],dp[i - 1][j - v1[i] - pri[i][2]] + p1[i] *v1[i] + pri[i][2] * w[i][2]);
}
if(j >= v1[i] +pri[i][1] +pri[i][2]) {
dp[i][j] = Math.max(dp[i][j],dp[i - 1][j - v1[i] - pri[i][2] - pri[i][1]] + p1[i] *v1[i] + pri[i][2] * w[i][2] + pri[i][1] * w[i][1]);
}
}
}
System.out.println(dp[cnt][n]);
}
}