【清华2019冬令营模拟12.15】打怪兽(1D1D + dP)

【清华2019冬令营模拟12.15】打怪兽

题意

  • 给定一个序列 { a i } \{a_i\} {ai},任意时刻可以积累价值 k k k,第 i i i时刻造成代价 max ⁡ ( a i − k , 0 ) \max(a_i-k,0) max(aik,0),若第 i i i时刻完之后 k > 0 k>0 k>0 k = k − 1 k=k-1 k=k1.

  • n n n个询问,第 i i i个询问答案为当总积累价值为 i i i时最小代价和.

  • n ≤ 4000 n\le 4000 n4000.

题解

  • 首先考虑一个十分简单的 d p dp dp,设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示到第 i i i时刻,总积累价值为 j j j,当前价值为 k k k的最小代价和.

  • 转移我们可以枚举 i − 1 i-1 i1时刻的总积累价值 p p p,这样时间复杂度是 O ( n 4 ) O(n^4) O(n4)的.

  • 但转移的时候要学会思考,要最大化运用已有决策,实际上对于 i − 1 i-1 i1时刻,我们只需用 f [ i − 1 ] [ j + 1 ] [ k ] → f [ i ] [ j ] [ k ] f[i-1][j+1][k]\rightarrow f[i][j][k] f[i1][j+1][k]f[i][j][k],以及 j = 0 j=0 j=0时的 f [ i − 1 ] [ j ] [ k ] → f [ i ] [ j ] [ k ] f[i-1][j][k]\rightarrow f[i][j][k] f[i1][j][k]f[i][j][k]转移,其他的,如果从 i − 1 → i i-1\rightarrow i i1i这一阶段有积累价值,那么我们可以用 f [ i ] [ j − 1 ] → f [ i ] [ j ] f[i][j-1]\rightarrow f[i][j] f[i][j1]f[i][j]转移过来,这样就充分利用了已经做好的决策,时间复杂度降为 O ( n 3 ) O(n^3) O(n3).

  • 当然,继续细心观察,我们发现,任何时刻积累代价都可以看做是互相独立的,否则如果两个积累代价产生的块状影响有相邻,那不如把后面的那个块状影响往前转移一些,答案不会更劣,也就是说,假设我在第 i i i时刻积累了 k 1 k_1 k1代价,在 j ( j > i ) j(j>i) j(j>i)时刻积累了 k 2 k_2 k2代价,且满足 i + k 1 > = j i+k_1>=j i+k1>=j,那不如直接在 i i i时刻积累 k 1 + k 2 k_1+k_2 k1+k2的代价.

  • 有了这个发现,我们就可以把状态写为二维的,我们令 f [ i ] [ j ] f[i][j] f[i][j]表示到 i i i时刻,积累的总代价为 j j j时的最小代价和,转移可以先从 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j]转移过来,表示从 i i i往后成为了一个独立块,或者枚举一个 p p p,由 f [ i − p ] [ j − p ] + C [ i − p + 1 ] [ p ] → f [ i ] [ j ] f[i-p][j-p]+C[i-p+1][p]\rightarrow f[i][j] f[ip][jp]+C[ip+1][p]f[i][j]转移过来,其中 C [ i ] [ j ] C[i][j] C[i][j]表示在第 i i i时刻积累 j j j价值的代价和,很显然, C [ i ] [ j ] C[i][j] C[i][j]是可以在 O ( n 2 ) O(n^2) O(n2)的时间内预处理出来的.

  • 那么现在,空间复杂度降到了 O ( n 2 ) O(n^2) O(n2),时间复杂度因为要枚举一个 p p p,实际上还是 O ( n 3 ) O(n^3) O(n3)的.

  • 然后我们尝试着去发现一些性质,显然, f [ i ] [ j ] f[i][j] f[i][j]显然当 i ≥ j i\ge j ij时才有意义,撇去 f [ i − 1 ] [ j ] → f [ i ] [ j ] f[i-1][j]\rightarrow f[i][j] f[i1][j]f[i][j]这个特殊转移,我们发现,其余的 f [ i − p ] [ j − p ] f[i-p][j-p] f[ip][jp]决策中 ( i − p ) − ( j − p ) = i − j (i-p)-(j-p)=i-j (ip)(jp)=ij,于是我们可以想到去按 i − j i-j ij分一下类,那么一个状态可以表示为 f [ i + d ] [ i ] f[i+d][i] f[i+d][i]的形式,然后考虑转移,那么就可以写成类似于这样的形式 f [ i + d ] [ i ] + C [ i + d ] [ z − i ] → f [ z + d ] [ z ] f[i+d][i] + C[i+d][z-i] \rightarrow f[z+d][z] f[i+d][i]+C[i+d][zi]f[z+d][z] f [ j + d ] [ j ] + C [ j + d ] [ z − j ] → f [ z + d ] [ z ] f[j+d][j]+C[j+d][z-j]\rightarrow f[z+d][z] f[j+d][j]+C[j+d][zj]f[z+d][z]

  • 然后考虑上面两种转移,可以发现,若 i > j i\gt j i>j,则 C [ j + d ] [ z − j ] ≥ C [ i + d ] [ z − i ] C[j+d][z-j]\ge C[i+d][z-i] C[j+d][zj]C[i+d][zi],且当 z z z不断增大时, C [ j + d ] [ z − j ] − C [ i + d ] [ z − i ] C[j+d][z-j]-C[i+d][z-i] C[j+d][zj]C[i+d][zi]会不断减小,那么这就像极了 1 d 1 d 1d1d 1d1d优化,我们可以用一个单调栈来维护最优决策,然后新增一个决策点时二分一下,时间复杂度可以做到 O ( n 2 l o g 2 n ) O(n^2log_2n) O(n2log2n)

  • 具体的来说,我们维护的是一个当前最优决策栈,也就是说,我们每次假设要更新 i + 1 i+1 i+1,那么栈顶的决策就是更新 f [ i + 1 + d ] [ i + 1 ] f[i+1+d][i+1] f[i+1+d][i+1]的最优决策.

  • 如何维护这个决策,我们不妨单独考虑两个决策 f [ i + d ] [ i ] f[i+d][i] f[i+d][i] f [ j + d ] [ j ] f[j+d][j] f[j+d][j],因为 i > j i>j i>j,所以我们发现,当决策 i i i优于决策 j j j时,一定是出现在某一个区间,于是我们可以二分出这个区间,那么就知道一个决策比上一个决策是在哪一段区间更优,超过这个区间,就把这个决策弹掉,那么这样栈顶就是最优决策了.

  • 总结一下,此题实质上运用的性质就是 C [ j + d ] [ k − j ] − C [ i + d ] [ k − i ] C[j+d][k-j]-C[i+d][k-i] C[j+d][kj]C[i+d][ki]随着 k k k的增大,这个 C [ j + d ] [ k − j ] − C [ i + d ] [ k − i ] C[j+d][k-j]-C[i+d][k-i] C[j+d][kj]C[i+d][ki]是不断减小的,于是可以类比 1 d 1 d 1d1d 1d1d优化进行一些骚操作.

你可能感兴趣的:(动态规划/DP,1D1D优化)