改变或扔掉元素,使两个数组相同的最小花费

给定两个长度分别为m和n的,元素均为整数的数组a[1..m]和b[1..n],有两种操作:

  1. 将某个数组中某个元素x的值加1或减1,花费为1;
  2. 将某个数组中某个值为0的元素扔掉,花费为0.

求使得这两个数组变得完全相同(需要考虑次序,即对于所有的i,有a[i] == b[i])的最小总花费。(已知m, n <= 2000,数组元素的绝对值<=10000)
如果将第2种操作中的花费改为1,又可以怎么做?

对于原问题

对于原问题,如果数组相同的定义不在乎次序的话(下标位置可交换),例如[1,2,3]和[2,1,3]这两个数组也是相同的,那么假设我们的a数组一定是比b数组长的(如果不是的话,交换一下次序就好了),那么说明我们一定会删除a数组m-n个元素

  • 然后我们将a数组和b数组分别排序(复杂度nlogn)
  • 然后循环b数组每个元素,并且找到一个未被标记且最接近的元素,假设b中的下标为i,找到的a中元素下标为j那么将这两个元素变为相等的花费为 abs(a[i]-b[j]) ,然后将a数组中下标为i的元素进行标记访问,这个复杂度为 m*n
  • 然后将剩下m-n个未被标记的元素进行操作到0,也就是对未标记的元素求和abs(a[i]),这就是将元素删除的花费操作,如果将第二种操作的花费改为1,其实也就是再加上了 m-n,并无太大区别
    总体复杂度为nlogn+mlogm+mn=mn

我目前能想到的做法是一种很慢的DP算法:

对于两个数组a和b,把它们操作成一样的最小花费记为min_cost(a, b).
①如果a和b一样长,
一种选择是一个不删,直接把a[i]一个一个调整成b[i],花费为sum_i |a[i] - b[i]|;
另一种选择是删掉a中的某一个元素a[i]得到新数组aa,再删掉b中的某一个元素b[j]得到新数组bb,此时aa和bb等长,可以递归调用min_cost,此时的花费为|a[i]| + |b[j]| + min_cost(aa, bb)
最终写出的状态转移方程为:

min_cost(a, b) = min(sum_i |a[i] - b[i]|, |a[i]| + |b[j]| + min_cost(a删掉a[i], b删掉b[j]) 对于所有的i和j)

②如果a和b不一样长,不妨让a长于b,那么a中一定有元素需要扔掉,于是可以写出:

min_cost(a, b) = min_i (|a[i]| + min_cost(a删掉a[i], b))

这样就可以写出暴力的分治法了。然后对算过的min_cost(a, b)做记录,就是一种备忘录式DP算法,但这个算法还是太慢了。