题目:
给定一个数组arr代表每个人的体重,给定一个正整数代表船的最大载重
求两两组合在不超重的情况下,最少需要多少只船能一次让所有人过河
分析:
1 类似二分法,将给定的最大载重量除以2,以得到的商为界,将arr划分成两个部分,左边都小于n/2,右边都大于N/2
2,经过1后,定义两个游标left,right,开始向两边+1循环,每次循环将left,right对应的元素加起来看是否满足最大载重,满足(小于n)就凑一对,将left,right对应的元素表为-99(随便一个arr里绝对不会出现的数,比如负数)
3,不满足(大于n),那就右边不动,左边先标记为-999(另外一个标记用的负数,表示这个元素是配对失败的,它只能跟左边剩余的元素配对)向左移,找更小的数满足就标记-99,不满足就标记-999,
4右边剩余元素如果不能与左边元素配对,它只能自己一只船,标记-999
5得到的数组元素只有-99和-999,-99肯定是偶数个,除以2 。-999有可能是奇数个,除以2向上取整,将两个商加起来就是最少的幢的数量
代码:
package main
import(
"fmt"
)
//给定一个数组arr代表每个人的体重,给定一个正整数代表船的最大载重
//求两两组合在不超重的情况下,最少需要多少只穿能一次让所有人过河
//list:=[]int{1,1,1,1,1,2,3,5,5,5,5,6,6,8,8,9,10}
func minboat(list []int,n int) int{
//先判断数组有一个体重大于最大载重的返回失败-1
match:=0
lowmatch:=0
list=Sort1(list)
fmt.Printf("length %v\n",len(list))
for i :=range(list){
if list[i]>n{
return -1
}
}
//写一个闭包返回left位置
left:=func() int{
for i :=range(list){
if list[i]>n/2{
return i-1
}
}
return 0
}()
// fmt.Println(left)
right:=left+1
// fmt.Printf("first %v %v \n",left,right)
for left>0 || right<len(list)-1{
//for循环加上必要的特定的条件,可以进入分支执行想要的步骤
//开始从left节点往左右迭代,直到left+right》n,负荷条件的标记为-99,
for list[left]+list[right]<=n && left>=0 && right<=len(list)-1{
// fmt.Printf("first for left : %v right: %v , value %v %v \n",left,right, list[left],list[right])
list[left],list[right]=-99,-99
//结束条件,两边都到达边界
if left==0 || right==len(list)-1{
break
}
left--
right++
}
//left+right如果大于n,说明右边不用动了,左边往左就是减小,因为左边的节点都是小于等于n/2的数,是可以两两配对
for list[left]+list[right]>=n && left>=0 && right<=len(list)-1{
// fmt.Printf("second for left:%v right: %v , value %v %v \n",left,right, list[left],list[right])
list[left]=-999
// left走到最左,说明此节点和right相加也是大于n的直接赋值-999
if left==0{
list[left]=-999
break
}
left--
// fmt.Printf("second left-- %v\n",left)
// fmt.Printf("second -- %v\n",list)
}
//经过上面两个遍历之后,数组变成[1 1 -99 -99 -99 -99 -99 -999 -999 -999 -999 -99 -99 -99 -99 -99 10]
//需要处理左边剩余配对失败的情况,剩余的元素从left开始递减,赋值为-999:
// for list[left]+list[right]> n && left-1>=0 && list[left]+list[left-1]<n{
for list[left]+list[right]> n && left-1>=0 {
list[left]=-999
//到达边界,赋为-999跳出
// fmt.Printf("333111111for %v \n",list)
if left==0{
list[left]=-999
break
}
// fmt.Printf("333for %v\n ",list)
left--
}
//经过第三个循环,左边已经没有元素了(list[left]<0),处理右边剩余的元素,右边剩余的元素只能单独乘船
//[-999 -99 -99 -99 -99 -99 -999 -999 -999 -999 -99 -99 -99 -99 -99 10 10]
for list[right]<=n && right<len(list)&& list[left]<0{
list[right]=-999
if right == len(list)-1{
break
}
right++
}
// fmt.Printf("endddddd left:%v right: %v , value %v %v \n",left,right, list[left],list[right])
// fmt.Println(list)
//-999是要向上取整的,-
}
for i :=range(list){
if list[i]==-99{
match++
}
if list[i]==-999{
lowmatch++
}
}
fmt.Println(list)
fmt.Printf("%v/2 + %v/2 + %v mod 2 \n ",match,lowmatch,lowmatch)
return match/2+lowmatch/2+lowmatch%2
}
//冒泡排序,两两对比,若后大于前则交换位置
func Sort1(list1 []int) []int{
//var list1 []int
// list1 := [10]int{4,2,25,14,13,27,11,15,30,45}
for i :=0;i<len(list1);i++{
for j :=i+1;j<len(list1);j++{
if list1[i]>list1[j]{
list1[i],list1[j] =list1[j],list1[i]
}
}
}
fmt.Println(list1)
return list1
}
func main(){
list:=[]int{1,1,1,1,2,3,5,5,5,5,6,6,8,8,9,10}
list1:=[]int{1,1,1,1,1,2,3,5,5,5,5,6,6,8,8,9,10}
list2:=[]int{1,1,1,1,2,3,5,5,5,5,6,6,8,8,9,10,10}
list3:=[]int{1,1,1,1,2,3,5,5,5,5,6,6,8,8,9,10,10,10}
fmt.Println(minboat(list,10))
fmt.Println(minboat(list1,10))
fmt.Println(minboat(list2,10))
fmt.Println(minboat(list3,12))
}