如何运用反向思维解决问题?

来源:互联网 发布:php怎么样 编辑:程序博客网 时间:2024/06/16 19:49

首先给出这样的一道题,这道题的大意是给一个边长为x的等边三角形,Memory同学每秒中都可以是该三角形其中的一个边长改变一次,但是必须保证是一个三角形,问题是最少经过几秒才能让边长为x的等边三角形变成边长为y的等边三角形。

首先想到的是模拟该过程,让每个边在保证是三角形的基础上逐个递减,最后达到要求,返回。那我们就在这个基础上思考改变次数最少的问题吧,设其中的边长分别为a,b,c, 如果第一次改变的是a,接着改变b,或者c, 这时候b或者c改变的幅度就和第一步改变的a有关系了,如果a的值比较大,那么(b-a)或者(c-a)的值就会比较小,从而c或者b的改变就会比较大。总结起来就是如果第一步a太小,后边b和c的变化幅度就会变小,如果a较大,a的变化又会多一步,这两种情况如何取舍,确是没有什么量化的方法。

如果改变思路,三角形变小完全就是三角形变大的逆过程,如果思考边长从较小的y变成x,每一步需要改变的变应该变的足够大,比如改变a,就应该让a=b+c-1,这样的做法不仅使得a收敛较快,并且也能给后续边的变化提供帮助,思路很棒!
实现也可以变的足够简单,可以用递归的方法写:

public class Solution3 {    public static void main(String[] args) {        // TODO Auto-generated method stub        Scanner sc = new Scanner(System.in);        int x = sc.nextInt(), y = sc.nextInt();        System.out.println(                deEvolution(new int[]{y,y,y},x));    }    public static int deEvolution(int[] arr, int x){        if(arr[0]>=x && arr[1]>=x && arr[2]>=x) return 0;        int t = arr[0] = arr[1] + arr[2] -1;        arr[0] = arr[1]; arr[1] = arr[2]; arr[2] = t;        return 1+deEvolution(arr,x);    }}

其实也可以用非递归的方法写,会更简单:

public class Solution3 {    public static void main(String[] args) {        // TODO Auto-generated method stub        Scanner sc = new Scanner(System.in);        int i,x = sc.nextInt(), y = sc.nextInt();        int arr[] = new int[]{y,y,y};        for(i = 0;arr[0]<x||arr[1]<x||arr[2]<x;i++){            arr[i%3] = arr[(i+1)%3]+arr[(i+2)%3]-1;        }        System.out.println(i);    }}

还有与之类似的一道题,就是给定一个数组,长度为n,每次只允许给其中的n-1个元素加1,问最少用几次加法使得整个数组的值是一致的。如果真的按照题目中的方法模拟,那将会非常麻烦。如果反过来思考,给n-1个元素加1其实是等价与给剩下的1个元素减一,减法做到直至所有的元素都相等为止。代码如下:

public class Solution {    public int minMoves(int[] nums) {        if (nums.length == 0) return 0;        int min = nums[0];        for (int n : nums) min = Math.min(min, n);        int res = 0;        for (int n : nums) res += n - min;        return res;    }}
0 0