目录
一、基础题:斐波那契数列
讲解:
斐波那契数列题目我没找,但是自己搞了一下下:
输入一个数N,让你求该数对应的数x;
这题其实都知道,f(n)=f(n-1)+f(n-2);
且已知前两项为1 1;则可以直接从第三项开始计算,直接一个for循环完事。
斐波那契数列进阶:进击的青蛙
初看这题:这和斐波那契数列有啥关系?
但是如果 你把斐波那契数列中的某几个值,让它固定为0,并且每一项为前三项和?然后再把初始已知值变只知道一个值1,不就和斐波那契数列一样了吗?
为什么会有一个已知值1?
因为你一开始的位置是可以往后移动的,就说明你初始所在位置不为0,且你前面没有其他的值了,那么比0大的最小整数就是1;
然后这题还要分类讨论一下:
当你走到第1个格子的时候,你只能从初始位置过去,所以f(1)=f(0);
当你走到第2个格子的时候,你可以从第一个格子+1,也可以从初始值+2;则f(2)=f(1)+f(0);
从第三个格子开始,都是前面三个数的叠加。
注意!!!如果该格子无法走到,无论前面值是多少,该格子始终为0!!!
进击的青蛙代码:
import java.util.Scanner;
public class 进击的青蛙 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();//有n个数据
int t=0;
int k=0;
long []brr=new long[n+1];
long a=1,b=0,c=0,d=1;
brr[0]=1;
for (int i=1;i<=n;i++){
t=sc.nextInt();//输入每一个数值
if (k<3) {//用于计算当前连续的石头个数是否进行运算
if (t == 1) {//当前位置为石头 则k+1;不然说明连续石头结束,k为0;
k++;
d=0;//走到当前位置的方法为0,因为不能走石头上
if (i>=3) {//只有当个数大于等于三时,才会需要前面的数值更改
//以下更改为每个数更改为后一个数 比如 a=1;b=2;c=3;d=0; 更改为 a=2,b=3,c=0;
//因为如果下一个数要计算的话 是跳1 2 3 ,则为当前的a b c或者说是更改前的b c d
a = b % 1000000007;
b = c % 1000000007;
c = d % 1000000007;
}
} else {// 当前值为0 说明可以跳到
k=0;//清空连续石头个数
if (i>=3){//如果 是大于等于三的位置,则为当前位置前三个的方法种数和
d=(c+b+a)%1000000007;
//求完和后 需要更改位置值,以便下一个值的计算
a=b%1000000007;
b=c%1000000007;
c=d%1000000007;
}else if (i==2){
//如果是2,则为初始位置和1号位置的种数和
c=(a+b)%1000000007;
}else{
//如果是1,则为初始位置的种数
b=(a)%1000000007;
}
}
}
}
if (k>=3||t==1){//说明有连续3个石头,或者最后一个位置是石头,不能跳,输出No Way!
System.out.println("No Way!");
return;
}
//输出最后的种数
System.out.println(d);
}
}
斐波那契数列变种:B君的寄望
这题一开始看:诶,不就是斐波那契数列每次+1或者+2,然后再加一个1嘛,只要到n就行了;
那么问题来了:假设长度为3,可以第一次短,第二次短;那能不能第一次长?
很明星,第一次长的话就会直接超了,因为你在停顿的那一秒已经到达了时间要求了;
还有想:那我选择+2后进行一次判断?这也不是不行,但是这样太累了!!!!
这题确实是斐波那契数列,但是难点在于:
将每一个给定值+1!!!!
每次可以+1或者+2,并且停顿1;那么我们看成+2/+3不就行了?那要到最后一个格子刚好是吹的时候,那么,我们只要让最后那个格子的后一个值是+2/+3后的位置不就可以了吗?
B君的寄望代码:
import java.math.BigInteger;
import java.util.Scanner;
public class B君的寄望 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt() + 1;
if (n==2||n==3||n==4){
System.out.println(1);
return;
}
BigInteger ai=new BigInteger(String.valueOf(1));
BigInteger bi=new BigInteger(String.valueOf(1));
BigInteger ci=new BigInteger(String.valueOf(1));
BigInteger di=new BigInteger(String.valueOf(1));
for (int i = 5; i <= n + 1; i++) {
ci=di;
di=ai.add(bi);
ai=bi;
bi=ci;
}
System.out.println(bi);
}
}
二、二维迷宫解:二维的斐波那契数列?
讲解:
二维迷宫解是什么??二维迷宫解就是:让你从某个位置走到另一个位置有多少种不同的走法,当然还有最短路走法,我们先不考虑最短路怎么走,我们先考虑有多少种不同的走法。
从A到B点,每次只能向下或者向右,有多少种走法?
我们可以发现:B点只能通过5号向下或者7号像右,所以f(b)=f(5)+f(7);
同样的可以求出每一个的数字对应的值;当然,这题我们要设置A点初始值为1,才能找到解。
为什么说这是二维的斐波那契数列呢?
因为它和斐波那契一样:每一个值都是通过前面得到的值获得;
A所在的行和列,都为1:因为它们的上边或者左边不存在数值,所以这两边的数值固定为1;
那么你就可以知道 f(4)=f(1)+f(3)=1+1=2;f(5)=(4)+f(2)=3;......这一行的数值都是前面的数值+1;
那么,f(4)所在的列和这边的数值也是相同的;
真题:过河卒
这题就是在上面的讲解中,固定了某几个点为0,走到这个点,无论多大,都归0;
过河卒代码:
基础版本:
import java.util.Scanner;
public class 过河卒 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int m=sc.nextInt();//n,m表示终点坐标
int a=sc.nextInt();
int b=sc.nextInt();//表示马位置坐标
int [][]arr=new int[n+1][m+1];
long [][]brr=new long[n+1][m+1];
arr[a][b]=1;
if (a-2>=0){
if (b-1>=0){
arr[a-2][b-1]=1;
}
if (b+1<=n){
arr[a-2][b+1]=1;
}
}
if (a+2<=m){
if (b-1>=0){
arr[a+2][b-1]=1;
}
if (b+1<=n){
arr[a+2][b+1]=1;
}
}
if (b-2>=0){
if (a-1>=0){
arr[a-1][b-2]=1;
}
if (a+1<=m){
arr[a+1][b-2]=1;
}
}
if (b+2<=n){
if (a-1>=0){
arr[a-1][b+2]=1;
}
if (a+1<=m){
arr[a+1][b+2]=1;
}
}
for (int i=0;i<=n;i++){
for (int j=0;j<=m;j++){
if (arr[i][j]==1){
brr[i][j]=0;
}else if (i==0&&j==0){
brr[i][j]=1;
} else if (i == 0) {
brr[i][j]=brr[i][j-1];
}else if (j==0){
brr[i][j]=brr[i-1][j];
}else{
brr[i][j]=brr[i-1][j]+brr[i][j-1];
}
}
}
System.out.println(brr[n][m]);
}
}
优化版本:
import java.util.Scanner;
public class 过河卒 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt()+2;
int m=sc.nextInt()+2;//终点位置
int a=sc.nextInt()+2;
int b=sc.nextInt()+2;//马的位置
int [][]arr=new int[n+3][m+3];
long [][]brr=new long[n+3][m+3];
arr[a][b]=1;arr[a+2][b+1]=1;arr[a+2][b-1]=1;arr[a-2][b+1]=1;arr[a-2][b-1]=1;
arr[a-1][b+2]=1;arr[a+1][b+2]=1;arr[a-1][b-2]=1;arr[a+1][b-2]=1;
brr[1][2]=1;
for (int i=2;i<n+3;i++){
for (int j=2;j<m+3;j++){
if (arr[i][j]==1){
brr[i][j]=0;
}
else
brr[i][j]=brr[i-1][j]+brr[i][j-1];
}
}
System.out.println(brr[n][m]);
}
}
三、迷宫最值:二维迷宫+贪心?
讲解:
这一类题型会让你找出最优解;
而我们知道,贪心算法是局部优解,而dp是全局优解,但是在很多情况下,dp+贪心所得到的答案,为正解!、
迷宫最值就像上面过河卒,将方案数改成最短距离,直接看题型吧:
基础真题:数字三角形
从下往上,要求取到的值总和最大,并且每次只能往上或者左上方走
但是这样的思考我个人觉得是有点点困难了,感觉复杂变化和边界值不好掌控;
个人思路:从上往下
因为题目要求到达山顶,那么最上面的值必定获得,然后就可以考虑:在它下面一层值的最大值怎么获得?
走到最上面要最大,那么只能从它的下一层的走到3/8时两个数的最大值中去选择;
这样子的话是从下往上去运算,dp转移公式:arr[a][b]+=Math.max(arr[a+1][b],arr[a+1][b+1]);
那么现在看一下从上往下运算:
为什么说附带贪心呢?因为在你的每一次选择中,你需要去保存可以得到的最大值
假设:现在你拥有6 你可以获得5个金币 也可以获得3个金币,那么你会选择哪一个?肯定会选择5个金币的吧?但是,这里又不能纯粹的使用贪心,就如图,我们通过答案可以知道,起始位置应该为从5开始往上走,但是如果选择单次最优,那么底部应该为6往上走;
那么怎么来确定当前保留值?
(自下往上的做法讲一下,从上往下的直接看我代码就好)
那么第倒数第二行开始,第一个位置为2,它可以通过最底层的4/5走到,这时候选择4/5中比较大的值,与其本身相加;然后判断7,7可以由5/2经过,选择5,然后后面的两个4,都选择的是6
这样 倒数第二层则为:7 12 10 10;
然后再往上一层用相同的方法:20 13 10;
再往上:23 21
最顶层就是在23/21中选择最大的那一个!!!
数字三角形代码:
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n=scanner.nextInt();
int [][]arr=new int[n][n];
for(int i=0;i<n;i++){
for(int j=0;j<=i;j++){
arr[i][j]=scanner.nextInt();
}
}
int s=arr[0][0];
for(int i=1;i<n;i++){
for(int j=0;j<=i;j++){
if(j==0)
arr[i][j]=arr[i-1][j]+arr[i][j];
else
arr[i][j]=Math.max(arr[i-1][j],arr[i-1][j-1])+arr[i][j];
}
}
Arrays.sort(arr[n-1]);
System.out.println(arr[n-1][n-1]);
}
}
基础题进阶:拿金币
看完上面的然后看到这个是否发现:好像?和上面的一样一样的?
没错,这题就是将上面的路径数和获取最大金币数结合了起来,不再是一个三角形的取金币;
那么这题的思路就很简单了,在每一种路径的可能下,选择最大金币数的路径,具体思路,从上面讲到这里了,也该自己思考一下了吧?毕竟看一篇文章,总得学会点什么?试试,万一就解出来了呢?代码就在下面,思路也在注释里,卡住了可以看看,不懂也可以问,自己思考一下这个题吧!
拿金币代码:
package com.LQBLX.xunlian;
import java.util.Scanner;
public class 拿金币 {
public static void main(String[] age){
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int a[][]=new int[n+1][n+1];//每一个位置的金币数
for (int i=1;i<=n;i++){//每一行
for (int j=1;j<=n;j++){//每一列
a[i][j]= sc.nextInt();
}
}
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
a[i][j]=a[i][j]+Math.max(a[i-1][j],a[i][j-1]);//加到这个值时,用可以走到这一步的大值去运算
}
}
System.out.println(a[n][n]);
}
}
最新模拟赛题:跳跃
这一题和上面的拿金币有区别吗?
我个人觉得是没有区别的,不要看它说一次可以走多少格子,你每一次走的时候,还是选择每一次的最优解即可,而每一个值对应的关系式可能会很长(因为它来自于9个位置的最大值!!!)
代码放在下面,看不懂的直接私信我,具体就不多讲解了,因为这属于上面拿金币的一点点加强版,本质上并没有太大区别。
跳跃代码:
import java.io.*;
public class 跳跃 {
static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
static StreamTokenizer st=new StreamTokenizer(br);
public static void main(String[] args) throws Exception{
int n=nextInt();
int m=nextInt();
int [][]arr=new int[n+3][m+3];
int t=nextInt();
for (int i=3;i<n+3;i++){
for (int j=3;j<m+3;j++) {
if (i == 3 && j == 3) {
arr[i][j] = t;
} else {
arr[i][j] = nextInt();
}
}
}
for (int i=0;i<n+3;i++){
for (int j=0;j<n+3;j++){
if (i<3||j<3){
arr[i][j]=arr[3][3];
}
}
}
for (int i=3;i<n+3;i++){
for (int j=3;j<m+3;j++) {
if (i == 3 && j == 3) {
arr[i][j]=arr[i][j];
} else {
arr[i][j] = arr[i][j] + Math.max(Math.max(Math.max(Math.max(arr[i - 1][j], arr[i - 2][j]), Math.max(arr[i - 3][j], arr[i][j - 1])), Math.max(Math.max(arr[i][j - 2], arr[i][j - 3]), Math.max(arr[i - 1][j - 1], arr[i - 1][j - 2]))), arr[i - 2][j - 1]);
}
}
}
System.out.println(arr[n+2][m+2]);
}
public static int nextInt() throws Exception{
st.nextToken();
return (int)st.nval;
}
}