算法6=最少数量的船载人过河问题

题目:
给定一个数组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))
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值