HDU-2829 Lawrence 斜率优化DP 题解与分析

● 本题解会有详细的分析,适合初学者阅读
⚠ 超长题目分析警告

原题

Problem Description

T. E. Lawrence was a controversial figure during World War I. He was a British officer who served in the Arabian theater and led a group of Arab nationals in guerilla strikes against the Ottoman Empire. His primary targets were the railroads. A highly fictionalized version of his exploits was presented in the blockbuster movie, “Lawrence of Arabia”.

You are to write a program to help Lawrence figure out how to best use his limited resources. You have some information from British Intelligence. First, the rail line is completely linear—there are no branches, no spurs. Next, British Intelligence has assigned a Strategic Importance to each depot—an integer from 1 to 5. A depot is of no use on its own, it only has value if it is connected to other depots. The Strategic Value of the entire railroad is calculated by adding up the products of the Strategic Values for every pair of depots that are connected, directly or indirectly, by the rail line. Consider this railroad:

img
Its Strategic Value is 45 + 41 + 42 + 51 + 52 + 12 = 49.
Now, suppose that Lawrence only has enough resources for one attack. He cannot attack the depots themselves—they are too well defended. He must attack the rail line between depots, in the middle of the desert. Consider what would happen if Lawrence attacked this rail line right in the middle:

img
The Strategic Value of the remaining railroad is 45 + 12 = 22. But, suppose Lawrence attacks between the 4 and 5 depots:

img
The Strategic Value of the remaining railroad is 51 + 52 + 1*2 = 17. This is Lawrence’s best option.

Given a description of a railroad and the number of attacks that Lawrence can perform, figure out the smallest Strategic Value that he can achieve for that railroad.

Input

There will be several data sets. Each data set will begin with a line with two integers, n and m. n is the number of depots on the railroad (1≤n≤1000), and m is the number of attacks Lawrence has resources for (0≤m<n). On the next line will be n integers, each from 1 to 5, indicating the Strategic Value of each depot in order. End of input will be marked by a line with n=0 and m=0, which should not be processed.

Output

For each data set, output a single integer, indicating the smallest Strategic Value for the railroad that Lawrence can achieve with his attacks. Output each integer in its own line.

题目翻译

Problem Description

历史背景自行阅读,与题目无关。。。光知道你要写个程序帮助这个叫La的玄学人就行了。。。

首先,铁路线是完全线性的——没有分支和岔道。接下来,某玄学部门给每个车段分配了一个战略价值(一个从1到5的整数)。车段本身没有用处,只有与其他车段连接时才有价值。整个铁路的战略价值是通过将铁路线直接或间接连接的每对车辆段的战略价值乘积相加来计算的。想想这条铁路:

其战略价值为 4 ∗ 5 + 4 ∗ 1 + 4 ∗ 2 + 5 ∗ 1 + 5 ∗ 2 + 1 ∗ 2 = 49 4*5+4*1+4*2+5*1+5*2+1*2=49 45+41+42+51+52+12=49

现在,假设La只有足够的资源进行一次攻击。他不能亲自攻击仓库,因为他们的防御太好了。他必须攻击沙漠中央的火车站之间的铁路线。想想如果劳伦斯袭击了这条铁路线中间会发生什么:

剩余铁路的战略价值为 4 ∗ 5 + 1 ∗ 2 = 22 4*5+1*2=22 45+12=22。但是,假设La袭击了4号和5号仓库:

剩余铁路的战略价值为 5 ∗ 1 + 5 ∗ 2 + 1 ∗ 2 = 17 5*1+5*2+1*2=17 51+52+12=17。这是La的最佳选择。

给出一条铁路的参数和La能发动的攻击次数,编写程序找出他能将该铁路的战略价值降低到最小的值。

Input

多组数据。

每组数据的第一行为两个整数,n和m。n是铁路上的车辆段数(1≤n≤1000),m是La有资源攻击的次数(0≤m<n)。下一行是n个整数,每个整数从1到5,依次表示每个仓库的战略价值,当输入的n、m都为0时,输入结束。

Output

对于每组数据,输出一个整数,表示La可以通过攻击实现的铁路的最小战略价值。

题目分析

本题目需要前导知识:DP优化-斜率优化以及四边形不等式优化(四边形不等式暂时不讨论)

但请放心:使用前导知识前,我们需要先通过一般手段解决这道题目。优化可以学完以后再做理解。

我们首先按照一般分析思路对题目进行分析:题目给出了一个长度为 n n n的序列,至多将序列分成 m + 1 m+1 m+1段。每段序列具有一个权值,权值的计算方法是闭区间内点权两两相乘之和。求整个序列权值最小和

首先我们需要一个数组储存点权,我们记为 v a l [ N ] val[N] val[N],为了后续计算方便,我们提前处理出前缀和。(关于前缀和不懂的请挪步:前缀和与差分)

前缀和我们记作 c n t [ N ] cnt[N] cnt[N],采用一层循环递推到底求出各点前缀和,至于为什么要处理出前缀和,请继续阅读:

由于本题是建立在区间的基础上进行分析,因此我们不光要有点权,还应该处理出区间权值和,我们将闭区间 [ i , j ] [i,j] [i,j]的权值和记作 c o s t [ i ] [ j ] cost[i][j] cost[i][j],我们举例来分析如何求区间权值。

设区间长度为 n = 4 n = 4 n=4,我们以题目叙述示例中的 4 , 5 , 1 , 2 4,5,1,2 4,5,1,2作为各点权值,记作 v a l [ 1 ] , v a l [ 2 ] , v a l [ 3 ] , v a l [ 4 ] val[1],val[2],val[3],val[4] val[1],val[2],val[3],val[4],那么 [ 1 , 4 ] [1,4] [1,4]区间的权值:
c o s t [ 1 ] [ 4 ] = v a l [ 4 ] ∗ v a l [ 3 ] + v a l [ 4 ] ∗ v a l [ 2 ] + v a l [ 4 ] ∗ v a l [ 1 ] + v a l [ 3 ] ∗ v a l [ 2 ] + v a l [ 3 ] ∗ v a l [ 1 ] + v a l [ 2 ] ∗ v a l [ 1 ] = v a l [ 4 ] ∗ ( v a l [ 1 ] + v a l [ 2 ] + v a l [ 3 ] ) + v a l [ 3 ] ∗ ( v a l [ 1 ] + v a l [ 2 ] ) + v a l [ 2 ] ∗ v a l [ 1 ] \begin{aligned} cost[1][4] &= val[4] * val[3] + val[4] * val[2] + val[4] * val[1] + val[3] * val[2] + val[3] * val[1] + val[2] * val[1]\\ &=val[4] * (val[1] + val[2] + val[3]) + val[3]*(val[1] + val[2]) + val[2] * val[1]\\ \end{aligned} cost[1][4]=val[4]val[3]+val[4]val[2]+val[4]val[1]+val[3]val[2]+val[3]val[1]+val[2]val[1]=val[4](val[1]+val[2]+val[3])+val[3](val[1]+val[2])+val[2]val[1]
同理, [ 1 , 3 ] [1,3] [1,3]区间的权值为:
c o s t [ 2 ] [ 4 ] = v a l [ 3 ] ∗ v a l [ 2 ] + v a l [ 3 ] ∗ v a l [ 1 ] + v a l [ 2 ] ∗ v a l [ 1 ] = v a l [ 3 ] ∗ ( v a l [ 2 ] + v a l [ 1 ] ) + v a l [ 2 ] ∗ v a l [ 1 ] \begin{aligned} cost[2][4] &= val[3] * val[2] + val[3] * val[1] + val[2] * val[1]\\ &=val[3]*(val[2] + val[1]) + val[2] * val[1]\\ \end{aligned} cost[2][4]=val[3]val[2]+val[3]val[1]+val[2]val[1]=val[3](val[2]+val[1])+val[2]val[1]
我们不难发现:当我们计算每段区间的权值时,会进行大量重复的计算。但是计算中重复的部分是固定的,因此,我们不妨将 [ 1 , 4 ] [1,4] [1,4]区间的计算式与 [ 1 , 3 ] [1,3] [1,3]区间计算式做差移项,可得:
c o s t [ 1 ] [ 4 ] = v a l [ 4 ] ∗ ( v a l [ 3 ] + v a l [ 2 ] + v a l [ 1 ] ) + c o s t [ 1 , 3 ] \begin{aligned} cost[1][4] &= val[4] * (val[3] + val[2] + val[1]) + cost[1,3]\\ \end{aligned} cost[1][4]=val[4](val[3]+val[2]+val[1])+cost[1,3]
这里我们又发现,乘项 ( v a l [ 3 ] + v a l [ 2 ] + v a l [ 1 ] ) (val[3] + val[2] + val[1]) (val[3]+val[2]+val[1])具有重复的求和性质,于是我们就想到了前缀和优化,避免重复进行这些无意义的加法。于是,上式再次被化简:
c o s t [ 1 ] [ 4 ] = v a l [ 4 ] ∗ ( c n t [ 3 ] − 0 ) + c o s t [ 1 , 3 ] \begin{aligned} cost[1][4] &= val[4] * (cnt[3] - 0) + cost[1,3]\\ \end{aligned} cost[1][4]=val[4](cnt[3]0)+cost[1,3]
为什么是 c n t [ 3 ] − 0 cnt[3] - 0 cnt[3]0?因为我们的举例区间是从头开始的,如果从 i − 1 i-1 i1开始,那么到 j j j的前缀和应表达为: c n t [ j − 1 ] − c n t [ i − 1 ] cnt[j-1] - cnt[i-1] cnt[j1]cnt[i1]

示例的值示意图如下:

1

经过计算,我们得出的 c o s t cost cost数组的值如下:

在这里插入图片描述

综上所述,我们对区间进行推广,可以得出任意区间 [ i , j ] [i,j] [i,j]的递推式:
c o s t [ i ] [ j ] = v a l [ j ] ∗ ( v a l [ j − 1 ] + v a l [ j − 2 ] + . . . + v a l [ i ] ) + v a l [ j − 1 ] ∗ ( v a l [ j − 2 ] + v a l [ j − 3 ] + . . . " v a l [ i ] " ) + . . . + v a l [ i − 1 ] ∗ v a l [ i ] = v a l [ j ] ∗ ( c n t [ j − 1 ] − c n t [ i − 1 ] ) + c o s t [ i ] [ j − 1 ] \begin{aligned} cost[i][j] &=val[j] * (val[j - 1] + val[j - 2] +...+val[i]) + val[j-1]*(val[j-2] + val[j-3] + ... " val[i]") + ...+val[i - 1] * val[i]\\ &=val[j] * (cnt[j - 1] - cnt[i - 1]) + cost[i][j - 1] \end{aligned} cost[i][j]=val[j](val[j1]+val[j2]+...+val[i])+val[j1](val[j2]+val[j3]+..."val[i]")+...+val[i1]val[i]=val[j](cnt[j1]cnt[i1])+cost[i][j1]
如果反推,同理可得另外一个递推式:
c o s t [ i ] [ j ] = c o s t [ i + 1 ] [ j ] + v a l [ i ] ∗ ( c n t [ j ] − c n t [ i ] ) cost[i][j] = cost[i + 1][j] + val[i] * (cnt[j] - cnt[i]) cost[i][j]=cost[i+1][j]+val[i](cnt[j]cnt[i])
到此,我们的准备工作结束,可以开始推导状态转移方程了。

回归题目,我们设 d p [ i ] [ j ] dp[i][j] dp[i][j]表示前 i i i个数,分成 j j j段能得到的最小序列权值和。

⚠ 闫氏DP分析法--“寻找最后一个状态不同的点”

状态是如何转移的?我们继续使用上面的一组示例进行模拟,并寻找最后一个状态不同的点,并根据这个点发生的状态转移推导方程:
d p [ 2 ] [ 1 ] = c o s t [ 1 ] [ 1 ] + c o s t [ 2 ] [ 2 ] d p [ 3 ] [ 1 ] = m i n ( ( c o s t [ 1 ] [ 1 ] + c o s t [ 2 ] [ 3 ] ) ,   ( c o s t [ 1 ] [ 2 ] + c o s t [ 3 ] [ 3 ] ) ) d p [ 3 ] [ 2 ] = c o s t [ 1 ] [ 1 ] + c o s t [ 2 ] [ 2 ] + c o s t [ 3 ] [ 3 ] d p [ 4 ] [ 1 ] = m i n ( ( c o s t [ 1 ] [ 1 ] + c o s t [ 2 ] [ 4 ] ) ,   ( c o s t [ 1 ] [ 2 ] + c o s t [ 3 ] [ 4 ] ) ,   ( c o s t [ 1 ] [ 3 ] + c o s t [ 4 ] [ 4 ] ) ) d p [ 4 ] [ 2 ] = m i n ( ( c o s t [ 1 ] [ 1 ] + c o s t [ 2 ] [ 2 ] + c o s t [ 3 ] [ 4 ] ) ,   ( c o s t [ 1 ] [ 1 ] + c o s t [ 2 ] [ 3 ] + c o s t [ 4 ] [ 4 ] ,   ( c o s t [ 1 ] [ 2 ] + c o s t [ 3 ] [ 3 ] + c o s t [ 4 ] [ 4 ] ) ) \begin{aligned} &dp[2][1] = cost[1][1] + cost[2][2]\\ &dp[3][1] = min((cost[1][1] + cost[2][3]),\ (cost[1][2] +cost[3][3])) \\ &dp[3][2] = cost[1][1] + cost[2][2] + cost[3][3]\\ &dp[4][1] = min((cost[1][1] + cost[2][4]),\ (cost[1][2] + cost[3][4]),\ (cost[1][3] + cost[4][4])) \\ &dp[4][2] = min((cost[1][1] + cost[2][2] + cost[3][4]),\ (cost[1][1] + cost[2][3] + cost[4][4],\ (cost[1][2] + cost[3][3] + cost[4][4])) \end{aligned} dp[2][1]=cost[1][1]+cost[2][2]dp[3][1]=min((cost[1][1]+cost[2][3]), (cost[1][2]+cost[3][3]))dp[3][2]=cost[1][1]+cost[2][2]+cost[3][3]dp[4][1]=min((cost[1][1]+cost[2][4]), (cost[1][2]+cost[3][4]), (cost[1][3]+cost[4][4]))dp[4][2]=min((cost[1][1]+cost[2][2]+cost[3][4]), (cost[1][1]+cost[2][3]+cost[4][4], (cost[1][2]+cost[3][3]+cost[4][4]))
我们将计算式进行类比,不难发现:

3

d p [ 4 ] [ 2 ] = m i n ( ( d p [ 2 ] [ 1 ] + c o s t [ 3 ] [ 4 ] ) ,   ( d p [ 3 ] [ 1 ] + c o s t [ 4 ] [ 4 ] ) ) dp[4][2] = min((dp[2][1] + cost[3][4]),\ (dp[3][1] + cost[4][4])) dp[4][2]=min((dp[2][1]+cost[3][4]), (dp[3][1]+cost[4][4]))
继续列举几组例子,观察最后一个转移点的变化,可以推导出状态转移方程(没啥用了,可以不推):
d p [ i ] [ j ] = m i n ( d p [ k ] [ j − 1 ] + c o s t [ k + 1 ] [ i ] ) ,   k = 1 , 2 , . . . , i − 1 dp[i][j] = min(dp[k][j - 1] + cost[k + 1][i]),\ k = 1,2,...,i-1 dp[i][j]=min(dp[k][j1]+cost[k+1][i]), k=1,2,...,i1

至此,动态规划推导部分结束,按照正常的思路,我们可以敲代码了。

此时不妨对时间复杂度进行估计:状态为 O ( n 2 ) O(n^2) O(n2),转移时间 O ( n ) O(n) O(n),总共 O ( n 3 ) O(n^3) O(n3),这不是一个优解算法,甚至会TLE。

当然,我们可以进行降维优化: d p [ i ] = m i n ( d p [ i ] , d p [ k ] + c o s t [ k + 1 ] [ i ] ) dp[i]=min(dp[i],dp[k]+cost[k+1][i]) dp[i]=min(dp[i],dp[k]+cost[k+1][i]),这里推荐一篇大佬的题解:HDU 3102 Lawrence of Arabia_yechenv,有兴趣可以继续探讨这种优化方案,在这种方案中,我们舍弃了对状态转移无贡献的维度。

😀斜率优化DP

抛开降维方案,我们回到超时的状态转移方程: d p [ i ] [ j ] = m i n ( d p [ k ] [ j − 1 ] + c o s t [ k + 1 ] [ i ] ) dp[i][j] = min(dp[k][j - 1] + cost[k + 1][i]) dp[i][j]=min(dp[k][j1]+cost[k+1][i])

c o s t [ i + 1 ] [ j ] cost[i + 1][j] cost[i+1][j]表示为$cost[1][j] - cost[1][i] -cnt[i]* (cnt[j] - cnt[a]) $,这个式子自己推,依然采用前文的方式列举、转式得到。

状态转移方程可以表示为: d p [ i ] [ j ] = m i n ( d p [ k ] [ j − 1 ] + c o s t [ 1 ] [ i ] − c o s t [ 1 ] [ k ] − c n t [ k ] ∗ ( c n t [ i ] − c n t [ k ] ) ) dp[i][j] = min(dp[k][j - 1] + cost[1][i] - cost[1][k] - cnt[k] *(cnt[i]-cnt[k]) ) dp[i][j]=min(dp[k][j1]+cost[1][i]cost[1][k]cnt[k](cnt[i]cnt[k]))

1 ≤ b < a ≤ i − 1 1 \leq b < a \leq i - 1 1b<ai1,则当决策a优于b时,存在 d p [ a ] [ j − 1 ] + c o s t [ 1 ] [ i ] − c o s t [ 1 ] [ a ] − c n t [ a ] ∗ ( c n t [ i ] − c n t [ a ] ) ≤ d p [ b ] [ j − 1 ] + c o s t [ 1 ] [ i ] − c o s t [ 1 ] [ b ] − c n t [ b ] ∗ ( c n t [ i ] − c n t [ b ] ) dp[a][j - 1] + cost[1][i] - cost[1][a] - cnt[a] *(cnt[i]-cnt[a]) \leq dp[b][j - 1] + cost[1][i] - cost[1][b] - cnt[b] *(cnt[i]-cnt[b]) dp[a][j1]+cost[1][i]cost[1][a]cnt[a](cnt[i]cnt[a])dp[b][j1]+cost[1][i]cost[1][b]cnt[b](cnt[i]cnt[b])

展开,移项得:
( d p [ a ] [ j − 1 ] + c o s t [ 1 ] [ a ] + c n t [ a ] 2 ) − ( d p [ b ] [ j − 1 ] + c o s t [ 1 ] [ b ] + c n t [ b ] 2 ) c n t [ a ] − c n t [ b ] ≤ c n t [ i ] \frac{(dp[a][j - 1] + cost[1][a] + cnt[a]^2) - (dp[b][j - 1] + cost[1][b] + cnt[b]^2)}{cnt[a] - cnt[b]} \leq cnt[i] cnt[a]cnt[b](dp[a][j1]+cost[1][a]+cnt[a]2)(dp[b][j1]+cost[1][b]+cnt[b]2)cnt[i]
在进行优化之前,我们枚举决策点 ( x i , y i ) (x_i, y_i) (xi,yi),将其视为二维平面上的点,并确定对应的 m i n ( d p [ k ] [ j − 1 ] + c o s t [ 1 ] [ i ] − c o s t [ 1 ] [ k ] − c n t [ k ] ∗ ( c n t [ i ] − c n t [ k ] ) ) min(dp[k][j - 1] + cost[1][i] - cost[1][k] - cnt[k] *(cnt[i]-cnt[k])) min(dp[k][j1]+cost[1][i]cost[1][k]cnt[k](cnt[i]cnt[k])),我们已经通过时间复杂度的角度分析认定它是一个“不高效的算法”。那么我们尝试对问题进行转化,即建立一个模型,减少所尝试的点,使得算法最优。这里就需要上述的不等式进行优化了。

在实际上,这些决策点的下端构成一个下凸包折线集,如图所示:

对于所谓的斜率优化,在本题中就是不停的维护下凸包折线,直至求出 d p [ i ] [ j ] dp[i][j] dp[i][j]的最小值。

设置循环枚举分段数,对于每个分段数(即原题目中炸毁的次数),我们维护一个双端单调队列,初始设置队首=队尾=0,对于每前n个范围之内的点维护队列,通过维护队列优选决策点,避免无意义的枚举。

**1.**对于一个凸包,每当 i i i增加时,会有一个新的点,而由于 x i x_i xi递增,所以新点 ( x i + 1 ,   y i + 1 ) (x_{i + 1}, \ y_{i + 1}) (xi+1, yi+1)必定位于右侧,位于凸包上,需要将这个点加入凸包,但是加入的时候必须符合斜率不断增加的定义,即: y i + 1 − y i x i + 1 − x i \frac {y_{i + 1}-y_i}{x_{i + 1 - x_i}} xi+1xiyi+1yi是递增的,所以可能会删除部分点,使其满足条件。当满足:
y i − y t x i − x t ≤ y t − y t − 1 x t − x t − 1 \frac{y_i - y_t}{x_i - x_t} \leq \frac {y_t - y_{t - 1}}{x_t - x_{t - 1}} xixtyiytxtxt1ytyt1
时,抛弃第 t t t个数对/点, t t t每次时点集的最后一项,重复这个过程,直至上述条件不再成立。注意:上述式子的条件是举例,本题有本题的不等式,即为 ( d p [ a ] [ j − 1 ] + c o s t [ 1 ] [ a ] + c n t [ a ] 2 ) − ( d p [ b ] [ j − 1 ] + c o s t [ 1 ] [ b ] + c n t [ b ] 2 ) c n t [ a ] − c n t [ b ] ≤ c n t [ i ] \frac{(dp[a][j - 1] + cost[1][a] + cnt[a]^2) - (dp[b][j - 1] + cost[1][b] + cnt[b]^2)}{cnt[a] - cnt[b]} \leq cnt[i] cnt[a]cnt[b](dp[a][j1]+cost[1][a]+cnt[a]2)(dp[b][j1]+cost[1][b]+cnt[b]2)cnt[i],我们同样重复抛弃t个数对/点,t每次为点集最后一项,直到条件不成立。

**2.**如果对于i=k, ( x j , y j ) (x_j, y_j) (xj,yj) 所对应的函数值大于 ( x j + 1 , y j + 1 ) (x_{j+1}, y_{j+1}) (xj+1,yj+1)所对应的函数值,即后者更优时,在所有 i < = k i <= k i<=k所对应的函数值都大于所对应 ( x j + 1 , y j + 1 ) (x_{j+1}, y_{j+1}) (xj+1,yj+1)的函数值,即后者都更优。我们需要满足过前者的直线与过后者的平行直线,前者纵截距>后者。

在两次维护之间,我们可以使用状态转移方程求出每个状态的最小决策,从而避免了赘余的计算,达到优化的效果。

AC Code

这里说明一下,我的状态转移方程的两个维度与下面代码的维度反了,因为的上面的分析写起来方便,所以置换了维度,原理不变化。

#include <bits/stdc++.h>
#define rnt register int
#define ll long long
using namespace std;
const int N = 1e3 + 10;
int n, m, Begin, End;
ll cnt[N], cost[N][N], val[N],dp[N][N], q[N];
inline ll getx(int k1, int k2){ 
    return cnt[k2] - cnt[k1]; 
}
inline ll gety(int i, int k1, int k2){
    return dp[i - 1][k2] - dp[i - 1][k1] + cost[1][k1] - cost[1][k2] + (cnt[k2] * cnt[k2]) - (cnt[k1] * cnt[k1]); 
}

int main(){
    ios::sync_with_stdio(0);
    while(cin >> n >> m, n + m){
        memset(cost, 0, sizeof(cost)), cnt[0] = 0;
        for (rnt i = 1; i <= n; i++) cin >> val[i];
        for (rnt i = 1; i <= n; i++) cnt[i] = cnt[i - 1] + val[i];
        for (rnt i = 1; i <= n; i++)
            for (rnt j = i + 1; j <= n; j++) cost[i][j] = cost[i][j - 1] + (cnt[j - 1] - cnt[i - 1]) * val[j];
        
        for (rnt i = 0; i <= n; i++) dp[0][i] = cost[1][i];
        for (rnt i = 1; i <= m; i++){
            Begin = End = 0;
            for (rnt j = i; j <= n; j++){
                while (Begin + 1 < End && gety(i, q[Begin], q[Begin + 1]) <= cnt[j] * getx(q[Begin], q[Begin + 1])) Begin++;
                if (Begin == End) dp[i][j] = 0;
                else dp[i][j] = dp[i - 1][q[Begin]] + cost[q[Begin] + 1][j];
                while (Begin + 1 < End && gety(i, q[End - 2], q[End - 1]) * 1.0 / getx(q[End - 2], q[End - 1]) >= gety(i, q[End - 1], j) * 1.0 / getx(q[End - 1], j)) End--;
                q[End++] = j;
            }
        }
        cout << dp[m][n] << endl;
    }
    return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HeartFireY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值