刷刷笔试题~~~[动态规划!!!!]

来源:互联网 发布:hdmi信号网络传输 编辑:程序博客网 时间:2024/04/30 10:37

动态规划算法

算法描述:

动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。基本思想也是将待求解问题分解成若干个子问题,先求解子问题,并将子问题的结果保存下来,然后从这些子问题的解得到原问题的解。

动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其它的算法


经常会遇到复杂问题不能简单地分解成几个子问题,而会分解出一系列的子问题。简单地采用把大问题分解成子问题,并综合子问题的解导出大问题的解的方法,问题求解耗时会按问题规模呈幂级数增加。

为了节约重复求相同子问题的时间,引入一个数组,不管它们是否对最终解有用,把所有子问题的解存于该数组中,这就是动态规划法所采用的基本方法。


1.硬币找零~~

问题描述:

现存在一堆面值为 V1、V2、V3 … 个单位的硬币,问最少需要多少个硬币才能找出总值为 T 个单位的零钱?
解题思路:
1,找出面值最接近T的硬币V
2,将f(T)问题的求解转换成f(T-V)+1问题的求解,以此出现递归
代码实现:

import java.util.Scanner;public class GreedyMax {public static void main(String[] args){Scanner sc=new Scanner(System.in);int n =sc.nextInt();int a[]=new int[n];for(int i=0;i<n;i++){a[i]=sc.nextInt();}String b[]=new String[n];for(int i=0;i<n;i++){//将整型转换成字串b[i]=Integer.toString(a[i]);}String temp="";for(int i=0;i<n;i++){for(int j=0;j<n-i-1;j++){if(b[j].compareTo(b[j+1])<=0){temp=b[j];b[j]=b[j+1];b[j+1]=temp;}}}String max="";for(int i=0;i<n;i++){max=max+b[i];}System.out.println(max);}}


2.换零钱~~

有一个数组changes,changes中所有的值都为正数且不重复。

每个值代表一种面值的货币,每种面值的货币可以使用任意张,对于一个给定值x,请设计一个高效算法,计算组成这个值的方案数。

给定一个int数组changes,代表所有零钱,同时给定它的大小n

另外给定一个正整数x,请返回组成x的方案数,保证n小于等于100且x小于等于10000。


public class Exchange {    public int countWays(int[] changes, int n, int x) {        // write code here        //dp[i][j]表示使用changes[0~i]的钱币组成金额j的方法数        int[][] dp=new int[n][x+1];        //第一列全为1,因为组成0元就只有一种方法        for(int i=0;i<n;i++)            dp[i][0]=1;        //第一行只有changes[0]的整数倍的金额才能有1种方法        for(int j=0;j*changes[0]<=x;j++){            dp[0][j*changes[0]]=1;        }        //从位置(1,1)开始遍历        for(int i=1;i<n;i++){            for(int j=1;j<=x;j++){                //关键:使用0~i的钱币组成j-changes[i]金额的方法数+使用0~i-1钱币组成j的方法数                dp[i][j]=dp[i-1][j]+(j-changes[i]>=0?dp[i][j-changes[i]]:0);//括号括号很重要!!            }        }                 return dp[n-1][x];    }    public static void main(String[] args){    int change[]={2,5,3,6};    int n=4;    int x=10;    Exchange ex=new Exchange();    System.out.println(ex.countWays(change, n, x));    }}


3.马戏团


4.合唱团

有 n 个学生站成一排,每个学生有一个能力值,牛牛想从这 n 个学生中按照顺序选取 k 名学生,要求相邻两个学生的位置编号的差不超过 d,使得这 k 个学生的能力值的乘积最大,你能返回最大的乘积吗? 

输入描述:
每个输入包含 1 个测试用例。每个测试数据的第一行包含一个整数 n (1 <= n <= 50),表示学生的个数,接下来的一行,包含 n 个整数,按顺序表示每个学生的能力值 ai(-50 <= ai <= 50)。接下来的一行包含两个整数,k 和 d (1 <= k <= 10, 1 <= d <= 50)。

输出描述:
输出一行表示最大的乘积。

输入例子:
3
7 4 7
2 50

输出例子:
49


解析:

因为有正有负,负负得正,所以要维护两个dp数组,一个存储最大,一个存储最小

定义fm[k][i]表示选中了k个学生,并以第i个学生为结尾,所产生的最大乘积

定义fn[k][i]表示选中了k个学生,并以第i个学生为结尾,所产生的最小乘积

那么fm[k+1][i+1]=max(fm[k][i]*stu[i+1],fn[k][i]*stu[i+1])

即当选中了k个学生后,再选择第i+1编号学生,所产生的最大积

但是并不能保证上一次选择的就是第i个学生,所以要遍历子数组fm[k]

另j从i到1,并且j与i+1之间小于间隔D,遍历fm[k][j],以及fn[k][j]

同理fn[k+1][i+1]=min((fm[k][i]*stu[i+1],fn[k][i]*stu[i+1])

最后,遍历一遍fm[k][i]求得最大值



5.
小东所在公司要发年终奖,而小东恰好获得了最高福利,他要在公司年会上参与一个抽奖游戏,游戏在一个6*6的棋

盘上进行,上面放着36个价值不等的礼物,每个小的棋盘上面放置着一个礼物,他需要从左上角开始游戏,每次只能

向下或者向右移动一步,到达右下角停止,一路上的格子里的礼物小东都能拿到,请设计一个算法使小东拿到价值最高的礼物。

给定一个6*6的矩阵board,其中每个元素为对应格子的礼物价值,左上角为[0,0],请返回能获得的最大价值,保证每个礼物价值大于100小于1000。

解题思路:
这是一个很简单的动态规划问题

import java.util.*;public class Bonus {    public int getMost(int[][] board) {    int  n=board.length;        int[][] sum=new int[n][n];        sum[0][0]=board[0][0];//能在外面初始化的都要记得初始化        for(int i=1;i<n;i++){//原来这两个放在里面一起来着,以后这种都放在外面先单独初始化        sum[0][i]=sum[0][i-1]+board[0][i];        sum[i][0]=sum[i-1][0]+board[i][0];        }        for(int i=1;i<n;i++){        for(int j=1;j<n;j++){        sum[i][j]=Math.max(sum[i-1][j], sum[i][j-1])+board[i][j];        }        }        return sum[n-1][n-1];    }}


6.股票交易日

在股市的交易日中,假设最多可进行两次买卖(即买和卖的次数均小于等于2),规则是必须一笔成交后进行另一笔(即买-卖-买-卖的顺序进行)。给出一天中的股票变化序列,请写一个程序计算一天可以获得的最大收益。请采用实践复杂度低的方法实现。

给定价格序列prices及它的长度n,请返回最大收益。保证长度小于等于500。

测试样例:
[10,22,5,75,65,80],6
返回:87

解析:

以第i天为分界线,计算第i天之前进行一次交易的最大收益profitBefore[i]

和第i天之后进行一次交易的最大收益profitAfter[i]

最后遍历一遍max{profit[i[+profitAfter[i]}

因为一共有两次交易‘分别找出第一次最大值,和第二次最大值

先从前往后遍历

因为要先买股票,找到花钱最少的

再一起找可以得到的最大收益,profitBefore[i],

用前一天的和今天新买股票进行比较


import java.util.*;public class Stock {    public int maxProfit(int[] prices, int n) {        if(n==0)            return n;        int[] profitBefore=new int[n];        int minBuy=prices[0];        profitBefore[0]=0;        for(int i=1;i<n;i++){            minBuy=Math.min(prices[i],minBuy);            profitBefore[i]=Math.max(profitBefore[i-1],prices[i]-minBuy);        }                int[] profitAfter=new int[n];        int maxSell=prices[n-1];        profitAfter[n-1]=0;        for(int i=n-2;i>=0;i--){            maxSell=Math.max(maxSell,prices[i]);            profitAfter[i]=Math.max(profitAfter[i+1],maxSell-prices[i]);        }        int max=0;        for(int i=0;i<n;i++){            max=Math.max(max,profitBefore[i]+profitAfter[i]);        }        return max;    }}


7.[编程题]跳石板

小易来到了一条石板路前,每块石板上从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,以空格隔开。(4 ≤ N ≤ 100000)(N ≤ M ≤ 100000)
输出描述:
输出小易最少需要跳跃的步数,如果不能到达输出-1
输入例子:
4 24
输出例子:
5

解析:

先写一个函数来求因数,这个方法求出的因数不包括1和他本身


import java.util.*;public class Main{    public static void main(String[] args){        Scanner sc=new Scanner(System.in);        while(sc.hasNext()){            int n=sc.nextInt();            int m=sc.nextInt();            System.out.println(Solution(n,m));        }    }    public static int Solution(int n,int m){    if(m==n)    return 0;    int[] dp=new int[m+1];            Arrays.fill(dp,Integer.MAX_VALUE);//放满最大值            dp[n]=0;            for(int i=n;i<=m;i++){                if(dp[i]==Integer.MAX_VALUE){                    dp[i]=0;                    continue;                }                ArrayList<Integer> list=Getlist(i);                for(int j=0;j<list.size();j++){                    int k=list.get(j);                    if(i+k<=m){//在合法范围内,                        dp[i+k]=Math.min(dp[i+k],dp[i]+1);//第一次到的时候是 到i的步数+1,之后再到达i+k的时候,取小的那个                    }                }            }            if(dp[m]==0)            return -1;            else                return dp[m];            }    //求因数    public static ArrayList<Integer> Getlist(int n){        ArrayList<Integer> list=new ArrayList<Integer>();    for(int i=2;i*i<=n;i++){//降低时间复杂度,劈成两半,只求一边,另一边就用n/i来求            if(n%i==0){                if(i!=1&&1!=n){//这里的因数不算1和自己本身                    list.add(i);                }                if(i*i!=n&&n/i!=1&&n/i!=n)//只从一半找,找到一个因数,其实就找到另一个因数                    list.add(n/i);            }        }        return list;    } }


8.题目描述:
你要出去旅游,有N元的预算住酒店,有M家酒店供你挑选,这些酒店都有价格X。 
需要你正好花完这N元住酒店(不能多,也不能少)最少能住几晚?
返回最少住的天数,没有匹配的返回-1* 
比如你有1000元,所有酒店都是大于1000的,则返回-1* 
比如你有1000元,有1家1000元的,有1家300,有1家700。则最少能住1晚,最多住2晚(300+700)。返回1* 
比如你有1000元,有1家387元,有1家2元,有一家611,则返回3(3家各住1天)* 
比如你有1000元,有1家1元的,有一家2元的,有一家1001元的,则返回500(1元的1000天,2元的500天)


输入
n个int,最后一个int为你拥有的钱,[0, n-2]为酒店的价格
输出
返回最少住的天数,没有匹配的返回-1


import java.util.*;public class Hotel {public static void main(String[] args){Scanner sc=new Scanner(System.in);String str=sc.nextLine();String[] s=str.split(" ");int[] arr=new int[s.length-1];int n=Integer.parseInt(s[s.length-1]);for(int i=0;i<arr.length;i++){arr[i]=Integer.parseInt(s[i]);}System.out.println(days(arr,n));}public static int days(int[] arr,int aim){if(arr.length==0||aim<=0)return -1;int n=arr.length;int max=Integer.MAX_VALUE;int[][] dp=new int[n][aim+1];for(int i=0;i<n;i++)dp[i][0]=0;for(int i=0;i<=aim;i++){dp[0][i]=i%arr[0]==0?i/arr[0]:max;}for(int i=1;i<n;i++){for(int j=1;j<=aim;j++){int left=max;if(j-arr[i]>=0&&dp[i][j-arr[i]]!=max)left=dp[i][j-arr[i]]+1;dp[i][j]=Math.min(dp[i-1][j], left);}}return dp[n-1][aim]==max?-1:dp[n-1][aim];}}


9.[编程题]蘑菇阵

现在有两个好友A和B,住在一片长有蘑菇的由n*m个方格组成的草地,A在(1,1),B在(n,m)。现在A想要拜访B,由于她只想去B的家,所以每次她只会走(i,j+1)或(i+1,j)这样的路线,在草地上有k个蘑菇种在格子里(多个蘑菇可能在同一方格),问:A如果每一步随机选择的话(若她在边界上,则只有一种选择),那么她不碰到蘑菇走到B的家的概率是多少?

输入描述:
第一行N,M,K(1 ≤ N,M ≤ 20, k ≤ 100),N,M为草地大小,接下来K行,每行两个整数x,y,代表(x,y)处有一个蘑菇。
输出描述:
输出一行,代表所求概率(保留到2位小数)
输入例子:
2 2 12 1
输出例子:
0.50

解析:

这道题不能用避开蘑菇的路径数/总路径数,因为概率是不同的

走不同路径的概率是不相等的。 
如   :
1 2 3
4 5 6
1->2 概率是0.5,2->3概率是0.5,3->6概率是1
1->2 概率是0.5,2->5概率是0.5,5->6概率是1
1->4 概率是0.5,4->5概率是   1,3->6概率是1
可以发现1-2-3-6与1-2-5-6的概率为0.25,而1-4-5-6概率为0.5
所以直接用可达路径数/总路径数求概率是不对的。

两个二维数组,一个map用来记录蘑菇在哪里,boolean类型的即可

一个double类型的dp  来记录到达这个点的概率是多少

行为i 列为j

首先如果该点有蘑菇,dp[i][j]=0,记得把dp[0][0]先刨除去

然后剩下的情况就是,原本dp[i][j]=dp[i-1][j]*概率+dp[i][j-1]*概率

但是i=0的时候,只有dp[i][j-1]*概率

j=0的时候,只有dp[i-1][j]*概率

i=n时,概率为1;j=m时,概率为1

其他时候概率为0.5

注意!!最后保留两位小数!!

String.format("%.2f",res)

import java.util.*;public class Main {public static void main(String[] args){Scanner sc=new Scanner(System.in);       while(sc.hasNext()){        int n=sc.nextInt();int m=sc.nextInt();int k=sc.nextInt();boolean[][] map=new boolean[n][m];for(int i=0;i<k;i++){int x=sc.nextInt();int y=sc.nextInt();map[x-1][y-1]=true;}double[][] dp=new double[n][m];dp[0][0]=1;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(map[i][j]){dp[i][j]=0;}else if(i==0&j==0){}else{dp[i][j]=(j==0?0:(i==n-1?dp[i][j-1]:dp[i][j-1]*0.5))+(i==0?0:(j==m-1?dp[i-1][j]:dp[i-1][j]*0.5));}}}double res=dp[n-1][m-1];System.out.println(String.format("%.2f", res));        }}}






0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 新生儿嘴巴很干怎么办 一岁宝宝过敏怎么办 母乳喂养宝宝不吃奶瓶怎么办 宝宝吃奶特别急怎么办 宝宝吃奶粉消化不良怎么办 喝羊奶大便干燥怎么办 宝宝换奶粉发烧怎么办 婴儿吃了蛋白怎么办 新生儿吃了蜂蜜怎么办 婴儿吃蜂蜜中毒怎么办 宝宝吃蜂蜜中毒怎么办 宝宝对蛋清过敏怎么办 宝宝吃蛋清过敏怎么办 婴儿吃蛋清过敏怎么办 初生婴儿拉水怎么办 婴儿鸡蛋过敏了怎么办 婴儿鸡蛋白过敏怎么办 母牛产后涨奶怎么办 宝宝吃奶时间长边吃边睡怎么办 宝宝吃海鲜过敏怎么办 小孩吃虾过敏怎么办 三个月婴儿不吃奶粉怎么办 宝宝秋季腹泻发烧怎么办 婴儿肚子有积食怎么办 小孩眼睛有点斜怎么办 孕妇血糖高便秘怎么办 宝宝蛋清过敏了怎么办 婴儿对鸡蛋过敏怎么办 宝宝断奶喝酸奶怎么办 宝宝被异物卡住怎么办 宝宝夜里膝盖痛怎么办 宝宝吃蛋白过敏怎么办 身体蛋白率低怎么办 孕妇喝酸奶呕吐怎么办 哺乳期得了寻麻疹怎么办 新生儿一定要抱睡怎么办 伊可新外壳吃了怎么办 Ddrop斜着倒了怎么办 ddrops滴多了怎么办 ddrops d3吃过量怎么办 ddrops吃多了怎么办