题目描述:
无线设备传输过程中,数据经常需要通过各种中继设备进行中转。现有某段传输路径,每隔1km放置1个中继设备用于数据中转,现用一数组来描述包括起始点的所有中继设备的最大传输距离(单位km)。求从起点到终点,能完成信号传输的最少中转次数;
输入:
一个数组: [2 ,3 , 1, 1]
该数组表示有4个中转站,其中元素表示每个中转站最大传输距离。
例如,上面数组表示第一台中转站最大传输距离位2,第二台中转站最大传输距离为3, 第三台中转站最大传输距离为1,第四台中转站最大传输距离为1。
输出:
从起点到终点,能完成信号传输的最少中转次数;
对于例1:
输入: 2 3 1 1
输出为: 2
说明:
1.最小中转方式:从1号设备中转到2号设备,2号设备直接到达4号设备
2.其他中转方式: 1号->2号-3号->4号, 需要3次中转;非最小;
拿到题目首先分析题目:
首先分析输入和输出。上文已经分析过了。
对于我当时的思考,当时就想用动态规划的思想,但是分析发现,最小子问题不具有最优子结构。因此就没有用动态规划,于是选择了用回溯算法。
回溯算法的思想就是,深度优先搜索,找到所有可能的解,从而找到最优。
先分析一个例子
当输入为[2, 3, 1, 1],其回溯图如下所示。
在这个题目中,最需要注意的就是每一层的搜索空间。下一层的搜索空间,取决于上一层的选择。
因为第一台中转站必须要经过,因此第一台不做讨论。当经过第二台时,他接着有两条路可以选择(3或 4 中转站)
因此就可以编写代码,其中用了trace记录每次选择的中转站编号。
/**
*
* @param nums
* @param
* @param trace 选择的路径
*/
public static void helper(int[] nums, LinkedList<Integer> trace){
// boundary
if (trace.getLast() == nums.length - 1){
max = Math.min(max, trace.size() - 1);
System.out.println(trace.toString()); // 打印选择的路径
return;
}
// search( 此处的搜索空间,就依赖于上一层选择的中转站)
for (int i = trace.getLast() + 1; i <= Math.min(nums[trace.getLast()] + trace.getLast(), nums.length-1); ++i){
trace.add(i);
helper(nums, trace );
trace.removeLast();
}
}
整个函数运行的代码为:
static int max = Integer.MAX_VALUE;
public static void main(String[] args) {
int[] nums = {2, 3, 1, 1};
LinkedList trace = new LinkedList<Integer>();
trace.add(0); // 初始值(因为中转站台1必选【对应数组索引0】)
helper(nums, trace);
System.out.println(max);
}
最后结果为:
上面表示选择的路径;
最下面一行表示最小中转次数为:2
多次测试
输入:
int[] nums = {4, 1, 2, 3, 1, 1, 1, 2, 3, 1, 1};
输出为:
经过运算,可以很清晰看到走过的路径。
但是整个计算量比较大。因此有没有优化的可能性?
因为每次我们都是逐个选择,并比较。
可以在添加利用一条规则,就是在每次的选择过程中,尽可能选择传输距离最远的站台,如果没有(即传输距离都一样),则选择当前站台可以达到的最远站台。【有点类似贪心算法,局部最优选择】
改进后的代码:
public static void helper(int[] nums, LinkedList<Integer> trace){
// boundary
if (trace.getLast() == nums.length - 1){
max = Math.min(max, trace.size() - 1);
System.out.println(trace.toString());
return;
}
int index = trace.getLast() + 1; // 起始选择站台
// search
for (int i = trace.getLast() + 1; i < Math.min(nums[trace.getLast()] + trace.getLast(), nums.length-1); ++i){
if (nums[i] <= nums[i + 1]){
index = i + 1; // 找到传输距离最远的站台
}
}
// 直接一步跳到最后一个站台
if (Math.min(nums[trace.getLast()] + trace.getLast(), nums.length-1) == nums.length-1){
index = nums.length -1;
}
// System.out.println("当前路径的最后一个:" + trace.getLast());
trace.add(index);
helper(nums, trace );
trace.removeLast();
}
测试
输入:
int[] nums = {4, 1, 2, 3, 1, 1, 1, 2, 3, 1, 1};(和上面测试实例相同)
输出为: