动态规划相关题目

一 袋鼠过河

题目描述:

一只袋鼠要从河这边跳到河对岸,河很宽,但是河中间打了很多桩子,每隔一米就有一个,每个桩子上都有一个弹簧,袋鼠跳到弹簧上就可以跳的更远。每个弹簧力量不同,用一个数字代表它的力量,如果弹簧力量为5,就代表袋鼠下一跳最多能够跳5米,如果为0,就会陷进去无法继续跳跃。河流一共N米宽,袋鼠初始位置就在第一个弹簧上面,要跳到最后一个弹簧之后就算过河了,给定每个弹簧的力量,求袋鼠最少需要多少跳能够到达对岸。如果无法到达输出-1

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int[] num=new int[n];
        for(int i=0;i<n;i++)
            num[i]=sc.nextInt();
        int[] jump=new int[n+1];
        for(int m=1;m<n+1;m++)
            jump[m]=10000;
        jump[0]=1;
         
        for(int k=1;k<=n;k++){
            //每次 看走到第k个地方的时候,走了多少步
            //走的步数与上一步的走法有关
            for(int j=k-1;j>=0;j--){
               if(num[j]==0)
                   continue;
                if(j+num[j]>=k)//从j的位置到k的位置是可达的,即一步能跳到
                    jump[k]=Math.min(jump[k],jump[j]+1);//则到位置k的步数取当前最小值
            }
        }
        if(jump[n]==10000)
            System.out.println(-1);
        else
            System.out.println(jump[n]-1);
         
    }
}

题目二:跳石板

题目描述:

小易来到了一条石板路前,每块石板上从1挨着编号为:1、2、3.......
这条石板路要根据特殊的规则才能前进:对于小易当前所在的编号为K的 石板,小易单次只能往前跳K的一个约数(不含1和K)步,即跳到K+X(X为K的一个非1和本身的约数)的位置。 小易当前处在编号为N的石板,他想跳到编号恰好为M的石板去,小易想知道最少需要跳跃几次可以到达。
例如:
N = 4,M = 24:
4->6->8->12->18->24
于是小易最少需要跳跃5次,就可以从4号石板跳到24号石板

/*动态规划的思想 状态转移方程为:steps[i+j] = min(steps[i]+1,steps[i+j])   //i为石板编号,j为i的约数 steps[N] = 0 */
import java.util.Scanner;
import java.util.ArrayList;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
        //维护一个整型数组,存放从初始位置到到每一个位置的所需要走的最少步骤
        //注意这里数组下标从0到n-1里面存的都是没有用的
        //除了下标为n的位置赋值为0 其余都赋值为最大值 
        int[] step=new int[m+1];
        for(int k=n+1;k<m+1;k++){
            step[k]=Integer.MAX_VALUE;
        }

        
           //这样写会超时 换一种思路:不是每次遍历之前的每一个位置然后判断其是否满足条件
           //而是直接看当前位置能到达的所有位置,即当前位置的所有约数+i 
           //相较于之前的思路 是一种从前到后的想法 之前是每一个位置 从后往前看
         /*   for(int i=n+2;i<=m;i++){
            
            //到每一个位置上的最少步数都与之前的走法有关
            for(int j=i-2;j>=n;j--){
                if(step[j]==Integer.MAX_VALUE)
                      continue;
                
               if(j%(i-j)==0 && i-j!=j){
                    //可达且约数不是本身也不是1,则最少步数取当前最短和上一步最短+1中的最小值
                    step[i]=Math.min(step[i],step[j]+1);
                }
            }
            }
            */
            //新思路实现:
        for(int i=n;i<=m-2;i++){
            //如果当前位置本身就不可达 则跳过
            //注意:这个方法是在这里判断的 因为当前位置已经在遍历之前位置的时候更新过了 如果还是最大值 则说明不可达
            //之前方法是从后往前更新的 所以不能上来就判断
            if(step[i]==Integer.MAX_VALUE)
                continue;
            ArrayList<Integer> factors=getFactors(i);//存放当前位置的所有约数
            //遍历所有约数
            for(int j=0;j<factors.size();j++){
                int temp=i+factors.get(j);
                if(temp<=m){
                    step[temp]=Math.min(step[temp],step[i]+1);
                }
            }
        }
        if(step[m]==Integer.MAX_VALUE){
            System.out.println(-1);
        }else{
            System.out.println(step[m]);
        }
    }
    //求除了本身和1之外的所有约数的函数
    public static ArrayList<Integer> getFactors(int n){
        ArrayList<Integer> res=new ArrayList<Integer>();
        //int bound=Math.sqrt(n);
        for(int i=2;i*i<=n;i++){
            if(n%i==0){
                res.add(i);
                if(i!=n/i)
                    res.add(n/i); 
            }
               
        }
        return res;
    }
}

题目三:网格的走法

题目描述:有一个X*Y的网格,小团要在此网格上从左上角到右下角,只能走格点且只能向右或向下走。请设计一个算法,计算小团有多少种走法。给定两个正整数int x,int y,请返回小团的走法数目。

import java.util.Scanner;
 
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        //String s=sc.next();
        //String[] str=s.split(",");
        //int x=Integer.parseInt(str[0]);
        //int y=Integer.parseInt(str[1]);
        //原来是动态规划。。。。不是深度优先搜索
        int x=sc.nextInt();
        int y=sc.nextInt();
        int[][]step=new int[11][11];
        int i=0;
        int j=0;
        for(i=1;i<=x;i++)
            step[i][0]=1;
        for(j=1;j<=y;j++)
            step[0][j]=1;
        i=1;
        j=1;
        for(int m=1;m<=x;m++){
            for(int n=1;n<=y;n++)
                step[m][n]=step[m-1][n]+step[m][n-1];
        }
 
        System.out.println(step[x][y]);
         
         
    }
     
}

题目四:数字和为sum的方法数

题目描述:

给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。

当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。 
import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int sum=sc.nextInt();
        int[] num=new int[n+1];
        for(int k=1;k<=n;k++)
            num[k]=sc.nextInt();
        
        System.out.println(dp(n,sum,num));
        
            
    }
    public static long dp(int n,int sum,int[] num){
        //利用动态规划的思想
        //二维数组dp dp[i][j]表示前i个元素构成和为j的方案数
        long[][] dp=new long[n+1][sum+1];
        //初始化dp
        //前i个元素构成和为0的方案数为1,即啥也不取
        for(int i=0;i<=n;i++){
            dp[i][0]=1;
        }
        //前0个元素构成和为任意值的方案数为0 即dp[0][0...n-1]为0,取默认值
        for(int i=1;i<=sum;i++)
            dp[0][i]=0;
        //正式开始dp啦
        for(int i=1;i<=n;i++){
            for(int j=1;j<=sum;j++){
                //递推公式:
                //如果第i个元素小于等于j,则分为取第i个元素和不取两种选择
                //此时前i个元素构成和为j的方案数 为前i-1个元素构成和为j(不取第i个元素)的方案数+前i-1个元素构成和为j-num[i]的方案数
                //如果第i个元素大于j 则只有一种情况了 就是不取第i个元素 
                if(num[i]<=j)
                    dp[i][j]=dp[i-1][j]+dp[i-1][j-num[i]];
                else
                    dp[i][j]=dp[i-1][j];
            }
        }
        return dp[n][sum];
    }
}
注意dp要声明为long型因为会溢出,另外num的下标和dp的下标要对应上 注意这里num是从下标1开始赋值的 一直赋值到下标为n;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值