动态规划(二)

动态规划

进阶版的一些题目心得体会和代码
核心:状态和状态转移

跳石板问题(来自牛客网)

问题描述:
小易来到了一条石板路前,每块石板上从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号石板

分析:
状态——记到N~M之间的石板A[j]为N+j块的步数。那么,约数就是转移状态的条件。除了A[0]=0,之外A[j]的初始记为最大值Integer.MAX_VALUE,遍历A[j],如果遇到不是最大值就计算可达的所有路径的A[j]的值。
需要注意的是,寻找约数的时候要优化算法,即找平方根一下的约数,然后直接把另一个对应的约数求出来。

代码如下:

import java.util.Scanner;
import java.util.ArrayList;
public class Main{
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        int M = scanner.nextInt();
        int[] path = new int[M-N+1];
        path[0] = 0;
        for(int i=1;i<path.length;i++){
            path[i] = Integer.MAX_VALUE;
        }
        for(int i=0;i<path.length;i++){
            if(path[i]==Integer.MAX_VALUE){
                continue;
            }else{
                for(int j=2;j*j<=N+i;j++){
                    if((N+i)%j==0){
                        if(i+j<M-N+1){
                            path[i+j] = Math.min(path[i]+1,path[i+j]);
                        }
                        if(i+((N+i)/j)<M-N+1){
                            path[i+((N+i)/j)] = Math.min(path[i]+1,path[i+((N+i)/j)]);
                        }
                    }
                }
            }
        }
        if(path[M-N]==Integer.MAX_VALUE){
            path[M-N]=-1;
        }
        System.out.println(path[M-N]);
         
    }
}

美丽的项链(来自牛客网)

问题描述:
妞妞参加了Nowcoder Girl女生编程挑战赛, 但是很遗憾, 她没能得到她最喜欢的黑天鹅水晶项链。
于是妞妞决定自己来制作一条美丽的项链。一条美丽的项链需要满足以下条件:
1、需要使用n种特定的水晶宝珠
2、第i种水晶宝珠的数量不能少于li颗, 也不能多于ri颗
3、一条美丽的项链由m颗宝珠组成
妞妞意识到满足条件的项链种数可能会很多, 所以希望你来帮助她计算一共有多少种制作美丽的项链的方案。

比如:
3 5
0 3
0 3
0 3
输出
12

分析:
状态有点难找,从简单的入手。
如果是用1种宝石,那么选j个就是1种方案。但是考虑到有上限和下限,所以只能在这之间为1种方案,其他为0.
如果是用2种宝石,那么选j个就是第1种宝石选0-j个,也就是说,方案是1种宝石的选择0-j个的方案和。但是考虑到有上限和下限,所以:
第2种宝石下限+第1种宝石选择i个<=j && 第2种宝石上限+第1种宝石选择i个>=j
从上述分析就可以大约想出,我们需要一个二维数组A[i][j],表示用i+1种宝石选择j个宝石的方案,即我们需要求的问题解。

代码如下:

import java.util.ArrayList;
import java.util.Scanner;
 
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();//kinds of diamonds
        int m = scanner.nextInt();//total num of diamods
        int[][] limit = new int[n][2];
        for(int i=0;i<n;i++){
            limit[i][0] = scanner.nextInt();
            limit[i][1] = scanner.nextInt();
        }
        long[][] result = new long[n][m+1];
        for(int i=0;i<m+1;i++){
            if(i>=limit[0][0]&&i<=limit[0][1]){
                result[0][i] = 1;
            }else{
                result[0][i] = 0;
            }
        }
        for(int i=0;i<n;i++){
            if(limit[i][0]<=0){
                result[i][0] = 1;
            }else{
                result[i][0] = 0;
            }
        }
        for(int i=1;i<n;i++){
            for(int j=1;j<m+1;j++){
                result[i][j] = 0;
                for(int k=0;k<=j;k++){
                    if(k+limit[i][1]>=j&&limit[i][0]+k<=j){
                        result[i][j]+=result[i-1][k];
                    }
                }
            }
        }
        System.out.println(result[n-1][m]);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值