A:你有写过瀑布流吗?
B:我写过等宽瀑布流。实现是当用户拉到底部的一定高度的时候,向后端请求一定数量的图片,然后再插入到页面中。
A:那我问一下,如何让几列图片之间的高度差最小?
B:这个需要后端发来的数据里面有图片的高度,然后我就可以看当前高度最小的是哪里列,将新图片插入那一列,然后再看看新的高度最小的是哪一列。
A:我觉得你没有理解我的问题,我的意思是如何给后端发来的图片排序,让几列图片之间的高度差最小?
B:(想了一段时间)对不起,这个问题我没有思路。
转化一下问题,将任意数分成特定的组,使得每组的和尽量相等
那么怎么解决这个问题呢?
记得刚接触编程的时候,有人和我说过写函数就是找规律,找到内部的共同点,我们现在就按这个方法尝试探究一下这个问题
不考虑分成一组的情况,我们直接考虑两组
在两组的情况下,很容易可以想到只要将其中一组尽可能拿到总和的1/2即可
那么这个问题就被转化为了01背包问题
条件:数组中的数就是背包问题的weight值,数组中的数也是背包问题的value值,即二者一样。
问题:背包里装哪些物品,使得其价值之和最接近总价值的一半。
01背包的处理取决于拿与不拿
简单陈述通过动态规划来求解0-1背包问题的思路。
用f(1,2,3,4....i)(j)表示背包为j时能在i个物品中取出的最大价值
用w(i),v(i)表示第i个物品的重量个价值
拿的表达式 f(1,2,3....i-1)(j-w(i))+v(i)
不拿的表达式 f(1,2,3....i-1)(j)
获得状态转移方程f(i)(j)=max(f(i-1)(j-w(i))+v(i),f(i-1)(j)) 1,2,3...i省略成i
chooseOrNot=(List, halfNum)=>{
let len = List.length
let f = new Array(len)
f[-1] = new Array(halfNum+1).fill(0) //使f[0-1][j]存在
let selected = []
for(let i = 0 ; i < len ; i++){
f[i] = [] //创建二维数组
for(let j=0; j<=halfNum; j++){
if( j < List[i] ){
f[i][j] = f[i-1][j] //物体比背包大
}else{
f[i][j] = Math.max(f[i-1][j], f[i-1][j-List[i]]+List[i])
}
}
}
let j = halfNum, w = 0
for(let i=len-1; i>=0; i--){
if(f[i][j] > f[i-1][j]){
selected.push(i)
j = j - List[i]
w += List[i]
}
}
return [f[len-1][halfNum], selected]
}
运用上述的方法,我们能不能获得将数组均分为三组的最优解呢?
我将之前的例子[2,3,4,5,6]带入得到了 [2,4],[5],[3,9]很显然这并不是最优解。
然后将任意数分成三组,使得每组的和尽量相等这个问题我陷入了两天的思考,搞得我浑身难受
最后我把这个问题抛给了我的组合数学老师
然后老师告诉我这个问题是NP完全的问题 https://en.wikipedia.org/wiki/3-partition_problem
居然一下子碰到了数学的天花板,世界数学七大难题之一。。。
回到'三体问题',贪心算法在数据比较均匀的时候不失为一种解决方法
模仿儿童为游戏选择团队的方式的问题的一种方法是贪婪算法,其以降序迭代数字,将它们中的每一个分配给具有较小总和的任何子集。该方法的运行时间为O(n log n)。当该集合中的数字与其基数大小相同或更小时,该启发式在实践中运行良好,但不能保证产生最佳可能的分区。
也就是我们常写的瀑布流,当然需要排序一下,比较简单直接拓展到n了
threeBody = (array,n) => {
array.sort((a, b) => {
return b - a
})
let list=[]
for (let j = 0; j < n; j++) {
list[j] = []
}
let arr=[]
for (let i = 0; i < array.length; i++) {
for (let j = 0; j < n; j++) {
arr[j]=sum(list[j])
}
min = Math.min(...arr)
for (let j = 0; j < n; j++) {
if(min==arr[j]){
list[j].push(array[i])
break
}
}
}
return(list,arr)
}
既然这样,我们之前抛弃的方法显然也是一种贪心算法,每次尽量取出1/n,感兴趣的,可以去比较一下这几种方法
当然这类问题还有适用于不同情境的算法
例如分治求解法 这里贴一篇论文 http://journal.nudt.edu.cn/publish_article/2004/6/200406024.pdf