link 一开始有属性a和b,这两个属性初始的时候均为0,每一天可以让a涨1点或b涨1点。我们共有 n n n 种奖励,第i种奖励有 x i , y i , z i x_i,y_i,z_i xi,yi,zi 三种属性,若 a ≥ x i a≥ x_i a≥xi 且 b ≥ y i b≥ y_i b≥yi,则弱弱在接下来的每一天都可以得到 z i z_i zi 的分数。 问 m m m 天以后弱弱最多能得到多少分数,其中 1 ≤ n ≤ 1000 , 1 ≤ m ≤ 2 e 9 , x i , y i ≤ 1 e 9 1 ≤ n ≤ 1000, \ 1 ≤ m ≤ 2e9, \ x_i, y_i ≤ 1e9 1≤n≤1000, 1≤m≤2e9, xi,yi≤1e9。
当 m m m 很小时,可以想到动态规划的方法。若 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示 i + j i + j i+j 天且 a = i , b = j a = i, \ b = j a=i, b=j 时得到的最大奖励,那么这个值肯定和之前状态有关, 若用 v [ i ] [ j ] v[i][j] v[i][j] 表示 a = i , b = j a = i, \ b = j a=i, b=j 时这一天可以得到的奖励,那么可以得到这样的状态转移方程: d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) + v [ i ] [ j ] dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + v[i][j] dp[i][j]=max(dp[i−1][j],dp[i][j−1])+v[i][j] 而对于 v [ i ] [ j ] v[i][j] v[i][j], 我们可以用前缀和的方法进行求解,若是初始时 v [ i ] [ j ] v[i][j] v[i][j] 表示 x = i , b = j x = i, \ b = j x=i, b=j 的属性的 z z z 值,可以得到这样的状态转移方程: v [ i ] [ j ] + = v [ i − 1 ] [ j ] + v [ i ] [ j − 1 ] − v [ i − 1 ] [ j − 1 ] v[i][j] += v[i-1][j] + v[i][j-1] - v[i-1][j-1] v[i][j]+=v[i−1][j]+v[i][j−1]−v[i−1][j−1] 这个可以类比一维的求前缀和得到,也可以简单地类比面积计算得到: 若 S S S 初始表示右上那一块小的,若要表示大长方形面积,对应 v [ i ] [ j ] v[i][j] v[i][j], S 1 S_1 S1 对应左边两块,对应 v [ i − 1 ] [ j ] v[i-1][j] v[i−1][j], S 2 S_2 S2 对应下边两块,对应 v [ i ] [ j − 1 ] v[i][j-1] v[i][j−1], S 3 S_3 S3 对应左下那块,对应 v [ i − 1 ] [ j − 1 ] v[i-1][j-1] v[i−1][j−1], 则 S + = S 1 + S 2 − S 3 S += S_1 + S_2 - S_3 S+=S1+S2−S3,对应于上述求前缀和转移式。 而对于ans: a n s = m a x ( a n s , d p [ i ] [ j ] + v [ i ] [ j ] ∗ ( m − i − j ) ) ( i + j ≥ m ) ans = max(ans, dp[i][j] + v[i][j] * (m - i - j) ) \ \ \ (i + j ≥ m) ans=max(ans,dp[i][j]+v[i][j]∗(m−i−j)) (i+j≥m) 而这一题 m m m 的范围是很大的,通过直接枚举天数进行dp肯定会超时。所以这时候可以想到离散化。
比如当有3个奖励: ( 100 , 100 , 1 ) , ( 20 , 30 , 2 ) , ( 30 , 20 , 3 ) (100, 100, 1), (20, 30, 2), (30, 20, 3) (100,100,1),(20,30,2),(30,20,3),对于 d p [ 100 ] [ 100 ] dp[100][100] dp[100][100], 只需要考虑 d p [ 20 ] [ 30 ] , d p [ 30 ] [ 20 ] , d p [ 30 , 30 ] dp[20][30], dp[30][20], dp[30, 30] dp[20][30],dp[30][20],dp[30,30] 这些值怎么变化到 d p [ 100 ] [ 100 ] dp[100][100] dp[100][100] 就好了,而无需从 d p [ 100 ] [ 99 ] , d p [ 99 ] [ 100 ] dp[100][99], dp[99][100] dp[100][99],dp[99][100] 这些值得到,因此我们只需要考虑这些奖励的 x i , y i x_i, y_i xi,yi 所组成的结点就好了。为此我们需要将 x i , y i x_i, y_i xi,yi 离散化:
scanf("%d %d", &n, &m); for(int i = 1; i <= n; i++) { scanf("%d %d %d", &pro[i].x, &pro[i].y, &pro[i].z); X[i] = pro[i].x; Y[i] = pro[i].y; } //先排序 sort(X + 1, X + 1 + n); //先排序 sort(Y + 1, Y + 1 + n); //再去重,得到去重后的元素个数 cnt1 = unique(X + 1, X + 1 + n) - (X + 1); cnt2 = unique(Y + 1, Y + 1 + n) - (Y + 1); for(int i = 1; i <= n; i++) { //将值从小到大排序后,依次映射到1, 2.... int x = lower_bound(X + 1, X + 1 + cnt1, pro[i].x) - X, y = lower_bound(Y + 1, Y + 1 + cnt2, pro[i].y) - Y; v[x][y] += pro[i].z; }其中 X [ i ] X[i] X[i] 中的值分别映射到 1 , 2... 1, 2 ... 1,2... 经过这样的映射之后,我们只对奖励里出现过的值进行遍历,比如上面的例子从 1 1 1 遍历到 100 100 100 可以变成 1 1 1 遍历到 3 3 3 (其中 1 1 1 对应 20 20 20, 2 2 2 对应 30 30 30, 3 3 3 对应 100 100 100),那么原来 d p [ i ] [ j ] dp[i][j] dp[i][j] 与 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j] 相差了 1 1 1 天, 现在就相差 X [ i ] − X [ i − 1 ] X[i] - X[i-1] X[i]−X[i−1] 天。 求 v [ i ] [ j ] v[i][j] v[i][j] 的转移方程没有变,而求 d p [ i ] [ j ] dp[i][j] dp[i][j] 的转移方程为: d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] + v [ i − 1 ] [ j ] ∗ ( X [ i ] − X [ i − 1 ] − 1 ) , d p [ i ] [ j − 1 ] + v [ i ] [ j − 1 ] ∗ ( Y [ j ] − Y [ j − 1 ] − 1 ) ) + v [ i ] [ j ] dp[i][j] = max(dp[i-1][j] + v[i-1][j] * (X[i] - X[i-1] - 1), dp[i][j-1] + v[i][j-1] * (Y[j] - Y[j-1] - 1)) + v[i][j] dp[i][j]=max(dp[i−1][j]+v[i−1][j]∗(X[i]−X[i−1]−1),dp[i][j−1]+v[i][j−1]∗(Y[j]−Y[j−1]−1))+v[i][j] 对于每一个 d p [ i ] [ j ] dp[i][j] dp[i][j],若 i + j ≤ m i + j ≤ m i+j≤m, 那么: a n s = m a x ( a n s , d p [ i ] [ j ] + ( m − X [ i ] − Y [ j ] ) ∗ v [ i ] [ j ] ) ans = max(ans, dp[i][j] + (m - X[i] - Y[j]) * v[i][j]) ans=max(ans,dp[i][j]+(m−X[i]−Y[j])∗v[i][j])
这样得到的即为需要的最大值,其中离散化操作为 O ( n l o g n ) O(nlogn) O(nlogn), 求前缀和,dp 为 O ( n 2 ) O(n^2) O(n2)
1.离散化的三步骤:①排序; ②去重; ③二分查找用下标对应值 2.二维求前缀和的方法