17分钟过桥 Java_java实现 n人过桥问题

java实现 n人过桥问题

##【问题描述】 n个人要晚上过桥,在任何时候最多两个人一组过桥,每组要有一只手电筒。在这n个人中只有一个手电筒能用,求这些人过桥所用的最短时间。

##【输入】

输入的第一行给出n,接下来的n行给出每个人的过桥时间

例如: 5 1 2 3 4 5

##【输出】

输出的第一行给出所有n个人过桥的总的秒数,接下来的若干行给出实现策略。每行包括一个或两个整数,表示组成一组过桥的一个或两个人,以所用的时间标识。

5

1 2 3 4 5

去: 1, 2

回: 1

去: 4, 5

回: 2

去: 1, 3

回: 1

去: 1, 2

最短时间: 16

##【问题分析】

当看到求最短、最长、最多、最少等字样的时候,大概率会是动态规划或者贪心算法的问题。n人过桥问题是比较典型的贪心算法,由于一次过桥最多两人且手电筒需要往返传递,因此以两个成员过桥为一个分析单位,计算过桥时间。首先按过桥时间顺序从小到大排序,nums[1]为最小,nums[n]为最大。

我们可以这么思考贪心算法的问题:将贪心问题分为多个子问题,这些子问题相互之间没有关联(动态规划的每一个子问题都会相互关联,这是与贪心算法的主要区别),且每个子问题的状态都是独立的。那么我们可以这样思考n人过桥问题:每个子问题所求的都是过去两人所花费的最短时间,且手电要回到起始点。贪心就贪在每次我都想让两个最费时的人过去。

基于上述思想,问题简化为如何求得最费时的两个人过去所花费的最短时间呢?这里有点拗口哈。有两种方案:

方案一:用最快的成员nums[1]传递手电筒帮助最慢的nums[n]和nums[n-1]过桥,易知来回所用的时间为2*nums[1]+nums[n]+nums[n-1]。

方案二:用最快的成员nums[1]和次快的成员nums[2]传递手电筒帮助最慢的nums[n]和nums[n-1]过桥,具体方案如下:

第一步:nums[1]和nums[2]到对岸,所用时间为nums[2];

第二步:nums[1]返回,将手电筒给最慢的nums[n]和nums[n-1],并且nums[n]和nums[n-1]到对岸后将手电筒交给nums[2],所用时间为:nums[1]+nums[n];

第三步:nums[2]返回,所用时间为nums[2];

综合起来方案二所用的总时间为2*nums[2]+nums[n]+nums[1]。

最后,不论是动态规划还是贪心算法,都需要考虑边界条件

显然,这里的边界条件是: 最后尚未过桥的人可能有3人或者2人,如果是3人,则最少过桥时间为nums[1]+nums[2]+nums[3],如果是2人,则是nums[1]+nums[2]。下面是算法实现:

public class Test {

public static void main(String[] args) {

Scanner sc = new Scanner(System.in);

int n = sc.nextInt(); // 读取人数

int[] nums = new int[n+1];

Arrays.sort(nums);

for (int i = 1; i <= n; i++) {

nums[i] = sc.nextInt();

}

int result = 0;

int stay = n;

while(stay > 3){

if(nums[1] + nums[stay-1] > 2*nums[2]) {

// 第二种方案

System.out.println("去: " + nums[1] + ", " + nums[2]);

System.out.println("回: " + nums[1]);

System.out.println("去: " + nums[stay-1] + ", " + nums[stay]);

System.out.println("回: " + nums[2]);

result += 2*nums[2] + nums[stay] + nums[1];

} else {

// 第一种方案

System.out.println("去: " + nums[1] + ", " + nums[stay]);

System.out.println("回: " + nums[1]);

System.out.println("去: " + nums[1] + ", " + nums[stay-1]);

System.out.println("去: " + nums[1]);

result += 2*nums[1] + nums[stay] + nums[stay-1];

}

stay -= 2;

}

if (stay == 3){

System.out.println("去: " + nums[1] + ", " + nums[3]);

System.out.println("回: " + nums[1]);

System.out.println("去: " + nums[1] + ", " + nums[2]);

result += (nums[1] + nums[2] + nums[3]);

} else {

System.out.println("去:" + nums[1] + ", " + nums[2]);

result += (nums[2]);

}

System.out.println("最短时间: " + result);

}

}

当然,如果不需要显示来回的过程,只需要把打印语句去掉即可,这样使代码变得简便:

public class Test {

public static void main(String[] args) {

Scanner sc = new Scanner(System.in);

int n = sc.nextInt(); // 读取人数

int[] nums = new int[n+1];

Arrays.sort(nums);

for (int i = 1; i <= n; i++) {

nums[i] = sc.nextInt();

}

int result = 0;

int stay = n;

while(stay > 3){

if(nums[1] + nums[stay-1] > 2*nums[2]) {

// 第二种方案

result += 2*nums[2] + nums[stay] + nums[1];

} else {

// 第一种方案

result += 2*nums[1] + nums[stay] + nums[stay-1];

}

stay -= 2;

}

if (stay == 3){

result += (nums[1] + nums[2] + nums[3]);

} else {

result += (nums[2]);

}

System.out.println("最短时间: " + result);

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值