动态规划-背包问题

动态规划可以分为状态表示和状态计算两个步骤。

01背包问题

题目描述

有 N件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000,0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例

8

题目分析

我们的目的是,找到一种选择方案,在背包能装下的情况下,使得选择物品的总价值最大,然后输出总价值。

用 g[i][j] 表示从前 i 种物品中进行选择,且总体积不大于 j 的各个选法获得的价值的集合。注意,g[i][j] 是个集合,表示一堆数:所有可能选出的价值。

例如 g[2][3] 从前 2 种物品中进行选择,且总体积不大于 3 的各个选法获得的价值的集合。选择方案有三种:都不选,价值为 0、选择第 1 个物品,价值为 2、选择第 2 个物品,价值为 4、选择第 1,第 2 个物品,价值为 6。 g[2][3] = {0, 2, 4, 6}。

用 f[i][j] 表示从前 i 种物品中进行选择,总体积小于等于 j 所能获得的最大价值, 注意,f[i][j] 是个一个数,是g[i][j] 这个集合中的最大值。很明显,f[i][j] 就是 g[i][j] 中的最大值。i j 取不同的值,就对应不同的 f[i][j],即对应不同的集合的最大值。

例如 f[2][3] 表示从前 2 种物品中进行选择,且总体积不大于 3 的获得的最大价值。f[2][3] = max(g[2][3]) = 6。

如果我们能把所有集合对应的最大值都求出来,即求出了 f[0][0] ~ f[N][V], f[N][V] 的含义是在前 N 种物品中进行选择,总体积不大于 V 所获得的最大价值,就是我们要找的答案。
为了求出f[i][j],我们可以使用下面的方法。

将 g[i][j] 这个集合划分成互斥的 A B 两部分。

A 部分是选法中不包含第 i 件物品。B 部分是选法中包含第 i 件物品。只要将 A 部分的最大值和 B 部分的最大值求出来,两者中较大的值就是 g[i][j] 的最大值,也就是 f[i][j]。。

A 部分,等价于从前 i - 1 件物品中选择,选出的物品总体积小于等于 j 的所有方案获得的价值集合,也就是 g[i - 1][j]。g[i - 1][j]这个集合中的最大值是 f[i - 1][j],所以 A 部分的最大值就是 f[i - 1][j]。

B 部分等价于从前 i 件物品中选择,并且必须选择第 i 件物品,且选出的物品总体积小于等于 j 的所有方案获得的价值集合。

B 部分怎么求呢?直接求不太好求:

因为 B 部分对应的方案中一定要选择第 i 件物品。这时候有两种情况。

给定的容量能放下第 i 件物品,那么第 i 件物品会占据 vi的背包空间,剩下的背包空间为 j - vi 。可以继续从前 i - 1 种物品中,选出的物品总体积小于等 j - vi的物品放入背包中。

从前 i - 1 种物品中进行选择,选出的物品总体积小于等 j - vi 的方案获得的价值集合为 g[i - 1][j - vi] 。所以 B 部分的元素为 g[i-1][j - vi] 中各个元素的值加上 wi 。g[i-1][j - vi] 中的最大值为 f[i-1][j - vi], 因此 B 部分的最大值为 f[i-1][j - vi] + wi 。

给定的容量不能能放下第 i 件物品,这时候背包里就不能放入第 i 件物品,因此 B 部分就是空集。B 部分的最大值为 0。

例如 g[2][3] 可以划分成两部分,A 部分是不包含第 2 种物品,对应方案1。B部分是包含第 2 种物品,对应方案 2 和方案 3。

A 部分的最大值是 f[2 - 1][3] = f[1][3] = 2。

B 部分的最大值是 f[2 - 1][3 - 2] + w2 = f[1][1] + 4 = 6。

所以集合g[2][3] 的最大值 f[2][3] = max(A,B) = max(2, 6) = 6。

通过上面分析,我们可以知道,g[i][j] 可以分成两部分,A 部分是不包含第 i 种物品对应所有选法的价值的集合,最大值是 f[i - 1][j]。B 部分是包含第 i 种物品对应所有选法获的价值的集合,最大值是 f[i-1][j - vi] + wi 或 0。所以 g[i][j] 的最大值就是在 A 部分的最大值与 B 部分的最大值取个max,也就是:
在这里插入图片描述
从计算公式可以看出,f[i][j] 是由 f[i - 1][j -vi ] 和 wi 计算出来的。也就是f[i][j]的值是可以从前面已经计算出的 f 值求出来。如果我们能确定 f[i][j] 的一部分初始值,就能通过该公式,一步步计算得出 f[N][V],也就是我们要找的答案。

01 背包问题的有些状态是能够直接确定的。

例如 f[0][0]。

f[0][0] 的含义是从前 0 件物品中选择,并且选出的物品总体积小于等于0 时所能得到的最大价值。总体积小于等于 0,说明一种物品都不能选择,因此 f[0][0] = 0。同理 f[1][0] = 0,f[2][0] = 0 ··· f[N][0] = 0。

有了这些初始值,通过 i 从 1 遍历 N,j 从 1 遍历 V,就能一步步求出所有的 f[i][j] 了。

例如求 f[1][1]。f[1][1] = max(A, B) = max{f[0][1],f[0][0] + 2} = max(0,2) = 2。

求 f[1][2]。f[1][2] = max(A, B) = max{f[0][2],f[0][0] + 2} = max(0,2) = 2。

最后 f[N][V] 就是要找的答案。

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];//v 保存体积,w 保存价值
int f[N][N];//保存所有集合最值状态
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )
        cin >> v[i] >> w[i];
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= m; j ++)
        {
            if(v[i] <= j)//能放入第 i 件物品的情况下,求f[i][j]
                f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
            else//不能放入第 i 件物品的情况下,求f[i][j]
                f[i][j] = f[i - 1][j];
        }
    }
    cout << f[n][m] << endl;//f[n][m] 就是答案
    return 0;
}

看一下 f[i][j] 的计算公式:f[i][j] = max(A, B)。

只用到了f[i - 1][j],f[i-1][j - vi] ,即只用到了 f[i - 1] 这一层,并且用到的体积为 j 和 j - vi,都是小于等于 j 的。

因此可以从体积为 V 开始,利用f[i - 1]的数据,求解出 f[i][j],把 f[i][j] 放到 f[i -1][j] 的位置上。这样 f 数组就能优化到一维了。

并且,当 背包容量小于 vj 的时候,f[i][j] = max{f[i - 1][j],0} = f[i - 1][j]。所以 j 只需要从 V 遍历到 vj 即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int f[N];
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++) cin>>v[i]>>w[i];
    for(int i=0;i<n;i++)
        for(int j=m;j>=v[i];j--)
            f[j]=max(f[j],f[j-v[i]]+w[i]);
    cout<<f[m]<<endl;
    return 0;
}

完全背包问题

问题描述

有 N种物品和一个容量是 V 的背包,每种物品都有无限件可用。
第 i 种物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000,0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例

10

问题分析

01 背包问题把 g[i][j]划分成了 A B 两部分,分别求出这两个部分对应的最大值,然后两者取最大值就是整体 g[i][j] 的最大值,就是 f[i][j]。

01 背包根据是否选择第 i 件物品,也就是第 i 件物品选 0 个还是 1 个,把 g[i][j] 划分成了 A B 两部分,分别求出这两个部分的最大值,然后两者取最大值就是整体 g[i][j] 的最大值,也就求出了 f[i][j]。

完全背包问题也是根据第 i 件物品的选择数量,把 g[i][j] 划分成不同的部分,分别求出各个部分的最大值,取各个部分最大值中的最大值,就是整体 g[i][j] 的最大值,也就求出了 f[i][j]。

因为每种物品的数量是无限的,根据第 i 种物品的选择数量可以把 g[i][j] 分为这样几部分:

A 部分: 第 i 种物品选 0 件。

B 部分:第 i 件物品选 1 件。

C 部分: 第 i 件物品选 2 件。

X 部分: 第 i 件物品选 x 件。

因为选择物品的总体积不能 j,所以第 i 件物品最多选 j / vi 向下取整件。

对于 A 部分:第 i 件物品选 0 件。等价于从前 i - 1 种物品中选择商品,且总体积不超过 j 的各个价值的集合,也就是 g[i - 1][j]。g[i - 1][j] 这个集合中的最大值是 f[i - 1][j] ,所以 A 部分的最大值就是 f[i - 1][j]。

对于 B 部分:第 i 件物品选 1 件, 1 个 i 物品会占据 vi的背包空间,剩下的背包空间为 j - vi 。可以从前 i - 1 种物品中,选出总体积小于等于j - vi 的物品放入背包。

从前 i - 1 种物品中,选出总体积小于等于j - vi的各个方案获得的价值集合为 g[i - 1][j - vi ],所以 B 部分的元素为 g[i - 1][j - vi] 中各个元素加上 wi。g[i - 1][j - vi ] 中的最大值为 f[i - 1][j - vi],所以 B 部分的最大值为 f[i - 1][j - vi] + wi。

对于 X 部分:第 i 件物品选 x 件, x 个 i 物品会占据 x * vi的背包空间,剩下的背包空间为 j - x * vi 。可以从前 i - 1 种物品中,选出总体积小于等于j - x * vi的物品放入背包。

从前 i - 1 种物品中,选出总体积小于等于j - x * vi 的各个方案获得的价值集合为 g[i - 1][j - x * vi ],所以 x 部分的元素为 g[i - 1][j - x * vi] 中各个元素加上 x * wi。g[i - 1][j - x * vi] 中的最大值为 f[i - 1][j - x * vi ],所以 B 部分的最大值为 f[i - 1][j - x * vi] + x * wi。

例如 g[2][4]。

第二种物品的体积为 2,选择物品的总体积不能超过 4。所以第二件物品可以选择:0件、1件、2件。

因此 g[2][4] 可以分成以下几部分:
A 部分:第二件物品选 0 件。A 部分的最大值为: f[i - 1][j - 0 * vi] + 0 * wi 。
B 部分:第二件物品选 1 件。B部分的最大值为: f[i - 1][j - 1 * vi ] + 1 * wi 。
C 部分:第二件物品选 2 件。C 部分的最大值为:f[i - 1][j - 2 * vi] + 2 * wi。
g[2][4] 中的最大值为 max(A,B,C)。

通过上面分析,我们可以知道,g[i][j] 可以分成若干部分:

A 部分是第 i 种物品选 0 个对应所有选法获的价值的集合,最大值是 f[i - 1][j]。
B 部分是第 i 种物品选 1 个对应所有选法获的价值的集合,最大值是 f[i-1][j - vi] + wi。
X 部分是第 i 种物品选 x 个对应所有选法获的价值的集合,最大值是 f[i - 1][j - x * wi]。
所以 g[i][j] 的最大值就是所有子集的最大值中最大的那个,也就是 f[i][j] = max(A, B ,····) 即:

展开式为:f[i] [j] = max( f[i-1][j] , f[i - 1][j - vi]+w , f[i - 1][j - 2 * vi] + 2 * w , f[i - 1][j - k * vi] + k * w , …) 其中 k <= j / w。
从计算公式可以看出:

f[i][j] 是由 f[i - 1][j - k * vi ] (0 <= k <= j / wi) 和 w 计算出来的。

f[i][j]的值是可以从前面已经计算出的 f[i-1] 值求出来。

如果我们能确定 f[i][j] 的一部分初始值,就能通过该公式,一步步计算得出 f[N][V],也就是我们要找的答案。

完全背包问题的有些状态是能够直接确定的。

例如 f[0][0]。

f[0][0] 的含义是:

从前 0 种物品中选择,并且选出的物品总体积小于等于0 时所能得到的最大价值。
总体积小于等于 0,说明一种物品都不能选择。
因此 f[0][0] = 0。同理 f[1][0] = 0,f[2][0] = 0 ··· f[N][0] = 0。
有了这些初始值,通过 i 从 1 遍历 N,j 从 1 遍历 V,第 i 种物品的选择数量 k 从 0 遍历到 j / wi 就能一步步求出所有的 f[i][j] 了。

例如求 f[1][1]:
f[1][1] = max{f[0][1],f[0][0] + 2} = max(0,2) = 2。

求 f[1][2]:
f[1][2] = max{f[0][2],f[0][1] + 2,f[0][0] + 4} = max(0,2,4) = 4。
最后 f[N][V] 就是要找的答案,代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int f[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
            for(int k=0;k*v[i]<=j;k++)
                f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
    cout<<f[n][m]<<endl;
    return 0;
}

上述代码会TLE,因此需要优化。

看一下 f[i][j] 和 f[i][j - vi] 的求解公式:

f[i][j] = max( f[i-1][j] , f[i-1][j - vi] + w , f[i-1][j - 2 * vi] + 2 * w , f[i-1][j - 3 * vi] + 3 * w , …)。
f[i][j - vi] = max( f[i-1][j - vi] , f[i-1][j - 2 * vi] + w , f[i-1][j - 3 * vi] + 2 * w , …) 。

由上两式,可得出如下递推关系:f[i][j] = max(f[i][j-v] + w , f[i-1][j]),代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int f[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
        {
            if(v[i]<=j) f[i][j]=max(f[i-1][j],f[i][j-v[i]]+w[i]);
            else f[i][j]=f[i-1][j];
        }
                
    cout<<f[n][m]<<endl;
    return 0;
}

上述式子和01背包很像,因此可以优化到一维。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int f[N];
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;i++) cin>>v[i]>>w[i];
    for(int i=0;i<n;i++)
        for(int j=v[i];j<=m;j++)
            f[j]=max(f[j],f[j-v[i]]+w[i]);
    cout<<f[m]<<endl;
    return 0;
}

多重背包问题1

问题描述

有 N 种物品和一个容量是 V 的背包。

第 i种物品最多有 si件,每件体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤100,0<vi,wi,si≤100

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例

10

问题分析

集合划分依据:根据第i个物品有多少个来划分.含0个、含1个···含k个.
状态表示与完全背包朴素代码一样均为:
f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);

代码

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int v[N],w[N],s[N];
int n,m;
int f[N][N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i]>>s[i];
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
            for(int k=0;k<=s[i] && k*v[i]<=j;k++)
                f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
    cout<<f[n][m]<<endl;
    return 0;
}

多重背包问题2

问题描述

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si件,每件体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N≤1000,0<V≤2000,0<vi,wi,si≤2000
提示:
本题考查多重背包的二进制优化方法。

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例

10

问题分析

疑惑1:为什么最后一项会是f[i−1,j−(S+1)v]+Sw

在完全背包中,通过两个状态转移方程:
f[i,j]=max(f[i−1,j],f[i−1,j−v]+w,f[i−1,j−2v]+2w,f[i−1,j−3v]+3w,…)
f[i,j−v]=max(f[i−1,j−v],f[i−1,j−2v]+w,f[i−1,j−2v]+2w,…)
通过上述比较,可以得到 f[i][j]=max(f[i−1][j],f[i][j−v]+w)。

再来看下多重背包,
f[i,j] = max(f[i−1,j],f[i−1,j−v]+w,f[i−1,j−2v]+2w,…f[i−1,j−Sv]+Sw,)= max(f[i−1,j−v],f[i−1,j−2v]+w,…f[i−1,j−Sv]+(S−1)w,f[i−1,j−(S+1)v]+Sw)

怎么比完全背包方程比较就多出了一项?
其实,一般从实际含义出发来考虑即可,这里是在分析f[i,j−w]
这个状态的表达式,首先这个状态的含义是 从前i个物品中选,且总体积不超过j-w的最大价值, 我们现在最多只能选s个物品,因此如果我们选s个第i个物品,那么体积上就要减去 s∗v,价值上就要加上s∗w,那更新到状态中去就是 f[i−1,j−v−s∗v]+s∗w

那为什么完全背包不会有最后一项?
完全背包由于对每种物品没有选择个数的限制,所以只要体积够用就可以一直选,没有最后一项。

疑惑2:为什么不能用像完全背包一样去优化?

因为MAX求得n个数最大值之后,不能用减法求n-1个数的最大值

疑惑3:二进制优化,它为什么正确,为什么合理,凭什么可以这样分?

我们首先确认三点:

(1)我们知道转化成01背包的基本思路就是:判断每件物品我是取了你好呢还是不取你好。
(2)我们知道任意一个实数可以由二进制数来表示,也就是其中一项或几项的和。
(3)这里多重背包问的就是每件物品取多少件可以获得最大价值。

分析:

如果直接遍历转化为01背包问题,是每次都拿一个来问,取了好还是不取好。那么根据数据范围,这样的时间复杂度是O(n3),也就是109,这样是毫无疑问是会TLE的。

假如10个取7个好,那么在实际的遍历过程中在第7个以后经过状态转移方程其实已经是选择“不取”好了。现在,用二进制思想将其分堆,分成k+1个分别有2k个的堆,然后拿这一堆一堆去问,我是取了好呢,还是不取好呢,经过dp选择之后,结果和拿一个一个来问的结果是完全一样的,因为dp选择的是最优结果,而根据第二点任意一个实数都可以用二进制来表示,如果最终选出来10个取7个是最优的在分堆的选择过程中分成了20=1,21=2,22=4,10−7=3 这四堆,然后去问四次,也就是拿去走dp状态转移方程,走的结果是第一堆1个,取了比不取好,第二堆2个,取了比不取好,第三堆四个,取了比不取好,第四堆8个,取了还不如不取,最后依旧是取了1+2+4=7个。

如果仍然不是很能理解的话,取这样一个例子:要求在一堆苹果选出n个苹果。我们传统的思维是一个一个地去选,选够n个苹果就停止。这样选择的次数就是n次

二进制优化思维就是:现在给出一堆苹果和10个箱子,选出n个苹果。将这一堆苹果分别按照1,2,4,8,16,…512

分到10个箱子里,那么由于任何一个数字x∈[0,1023]
(第11个箱子才能取到1024,评论区有讨论这个)都可以从这10个箱子里的苹果数量表示出来,但是这样选择的次数就是 ≤10次 。

比如:

  • 如果要拿1001次苹果,传统就是要拿1001次;二进制的思维,就是拿7个箱子就行(分别是装有512、256、128、64、32、8、1

代码

#include<bits/stdc++.h>
using namespace std;
const int N=25000;
int n,m;
int v[N],w[N];
int f[N];
int cnt;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int a,b,s;
        cin>>a>>b>>s;
        int k=1;
        while(k<=s)
        {
            cnt++;
            v[cnt]=k*a;
            w[cnt]=k*b;
            s-=k;
            k*=2;
        }
        if(s>0)
        {
            cnt++;
            v[cnt]=a*s;
            w[cnt]=b*s;
        }
    }
    n=cnt;
    for(int i=1;i<=n;i++)
        for(int j=m;j>=v[i];j--)
            f[j]=max(f[j],f[j-v[i]]+w[i]);
    cout<<f[m]<<endl;
    return 0;
}

分组背包

问题描述

有 N 组物品和一个容量是 V 的背包。

每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式

第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。

接下来有 N 组数据:

每组数据第一行有一个整数 Si,表示第 i个物品组的物品数量;
每组数据接下来有 Si行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤100,0<Si≤100,0<vij,wij≤100

输入样例

3 5
2
1 2
2 4
1
3 4
1
4 5

输出样例

8

问题分析

这道题和01背包差不多

对于每组 s 个物品,有 s+1 种选法:fj=max(fj,fj−v0+w0,fj−v1+w1,…,fj−vs+ws)
就是说可以不选(选 0个),选 1 个,选 2 个…选 s 个

所以,我们先循环枚举所有体积,再循环枚举所有选择,最后得出状态转移方程:fj=max(fj,fj−vk+wk),其中 k是枚举所有选择中的循环变量

代码

#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,m;
int v[N][N],w[N][N],s[N];
int f[N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>s[i];
        for(int j=0;j<s[i];j++)
            cin>>v[i][j]>>w[i][j];
    }
    for(int i=1;i<=n;i++)
        for(int j=m;j>=0;j--)
            for(int k=0;k<s[i];k++)
                if(v[i][k]<=j)
                    f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
    cout<<f[m]<<endl;
    return 0;
}
  • 41
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值