名企笔试:Google面试题-目标和

来源:互联网 发布:r语言数据挖掘 薛薇 编辑:程序博客网 时间:2024/06/08 19:15

Problem:
* 给你一个非负的数组数列a1,a2,…,an和一个期望值S。你可以为每一个整数赋值一个新的符号,
* 符号只能从+和-中选择。计算有多少种组合可以另赋过符号的所有数的和等于S。
* 输入样例:nums=[1,1,1,1,1],S=3
* 输出:5
分析:
所有分析见代码注释:
一组数据的算法时间对比:
/**
* 时间对比结果
* 30
* nums = {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
* S = 2
* dfs使用时间:5648
* answer = 145422675
* dfsplus使用时间:2643
* answer = 145422675
* dp使用时间:0
* answer = 145422675
*
*/
Code:

package google;import java.util.Date;import java.util.Scanner;/**  * @author xiaoran * Time: 2017-09-18 19:04 * Problem: * 给你一个非负的数组数列a1,a2,...,an和一个期望值S。你可以为每一个整数赋值一个新的符号, * 符号只能从+和-中选择。计算有多少种组合可以另赋过符号的所有数的和等于S。 * 输入样例:nums=[1,1,1,1,1],S=3 * 输出:5 */public class Google001 {    static int answer = 0;    /**     * 先使用暴力搜索,再进行剪枝。     * 对于第i个值,有2种选择,     * 第1种是选择赋值为+使用;第2种是选择赋值为-使用,     *      * 参数:     * nums:输入的原始的数组     * i:第i的值     * curSum:当值的和的值     * S:期望的目标的和     */    public static void dfs(int[] nums,int i,int curSum,int S){        int len = nums.length;//      System.out.println(len);        if(i == len){//注意长度是第一满足的条件            if(curSum == S){                answer++;            }            return ;        }        //第1种        dfs(nums,i+1,curSum+nums[i],S);        //第2种        dfs(nums,i+1,curSum-nums[i],S);    }    public static int[] getSum(int[] nums){        int len = nums.length;        int[] sum = new int[len];        sum[len-1] = nums[len-1];        for(int i=len-2;i>=0;i--){            sum[i] = sum[i+1] + nums[i];        }        return sum;    }    /**     *      * @param nums:输入的原始数组     * @param i:第i的位置     * @param curSum:当前的值     * @param S:期望的额值     * @param sum[]:后缀和的数组     * 在基础的dfs的基础上添加剪枝,降低时间     * 要明白什么是剪枝,搜索树在还没有到达叶节点的时候,就知道这条路是不通的,     * 于是提前结束,降低时间的消耗。我们就是要判断当前的值curSum是否会在后面达到S。     * sum[i]:从i-nums.length的和。     * 对于第i的位置时,如果curSum+sum[i] < S。则表示后面的全是正的也不能达到S。     * if curSum - sum[i] < S。全是负的也不行就要剪枝。     * 两种情况合并得到的S-curSum > Math.abs(sum[i])则进行剪枝     *      */    public static void dfsplus(int[] nums,int i,int curSum,int S,int[] sum){        int len = nums.length;//      System.out.println(len);        if(i == len){//注意长度是第一满足的条件            if(curSum == S){                answer++;            }            return ;        }        //判断是否需要剪枝        if(Math.abs(S - curSum) <= sum[i]){            //第1种            dfsplus(nums,i+1,curSum+nums[i],S,sum);            //第2种            dfsplus(nums,i+1,curSum-nums[i],S,sum);        }    }    /**     * @param args     * dfs是一种暴力的方法,剪枝只是一种手段,并不能使其从根本上降低时间。     * dp:动态规划问题就很厉害。     * dp[i,j]:表示前i个值组成j的种类的个数。     * 最后的结果就是dp[nums.length-1,S].     * 中间计算使用两层循环i和j。i:[0,nums.length],j:[0,S]     * 好像我们已经解决问题了,但是在运行的过程中j可能出现负数的情况。     * 如果我们对所有的j都加上sum,就可以解决这个问题,对应的结果就是     * sum:数组的和。     * 最后的结果就是dp[nums.length-1,S+sum]     * dp转化方程:     * dp[i,j+sum] 来源于两种情况dp[i-1,j+sum+nums[i]]和dp[i-1,j+sum-nums[i]     *      */    public static int findSways(int[] nums,int S){        int ans = 0,sum = 0;        for(int a: nums){            sum+=a;        }        if(sum < Math.abs(S)) return 0;//      System.out.println(sum<<1);        int dp[][] = new int[nums.length][sum<<1 + 1];        //dp起点需要注意的是nums[0]==0的情况,+nums[0],-nums[0]        if(0==nums[0]) dp[0][nums[0]+sum] = 2;        else{            dp[0][sum-nums[0]]=1;            dp[0][sum+nums[0]]=1;        }        for(int i=1;i<nums.length;i++){            for(int j=0;j<=sum<<1;j++){                if(j-nums[i]>=0) dp[i][j] += dp[i-1][j-nums[i]];                if(j+nums[i]<=sum<<1) dp[i][j] += dp[i-1][j+nums[i]];            }        }        return dp[nums.length-1][S+sum];    }    public static void main(String[] args) {        int n;        Scanner sc = new Scanner(System.in);        while(sc.hasNext()){            n = sc.nextInt();            int nums[] = new int[n];            for(int i=0;i<n;i++){                nums[i] = sc.nextInt();            }            int S = sc.nextInt();            answer = 0;            //测试运行时间            long start = new Date().getTime();            dfs(nums,0,0,S);            long end = new Date().getTime();            System.out.println("dfs使用时间:"+ (end - start));            System.out.println(answer);            answer = 0;            start = new Date().getTime();            int[] sum = getSum(nums);            dfsplus(nums,0,0,S,sum);            end = new Date().getTime();            System.out.println("dfsplus使用时间:"+ (end - start));            System.out.println(answer);            start = new Date().getTime();            int ans = findSways(nums,S);                        end = new Date().getTime();            System.out.println("dp使用时间:"+ (end - start));            System.out.println(ans);        }    }}/** * 时间对比结果 * 30 * nums = {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1} * S = 2 * dfs使用时间:5648 * answer = 145422675 * dfsplus使用时间:2643 * answer = 145422675 * dp使用时间:0 * answer = 145422675 *  */
原创粉丝点击