zoukankan      html  css  js  c++  java
  • 洛谷题单 算法1-4 递推与递归

    1 地毯填补问题

    题目描述
    相传在一个古老的阿拉伯国家里,有一座宫殿。宫殿里有个四四方方的格子迷宫,国王选择驸马的方法非常特殊,也非常简单:公主就站在其中一个方格子上,只要谁能用地毯将除公主站立的地方外的所有地方盖上,美丽漂亮聪慧的公主就是他的人了。公主这一个方格不能用地毯盖住,毯子的形状有所规定,只能有四种选择(如图):
    在这里插入图片描述

    并且每一方格只能用一层地毯,迷宫的大小为 2^k* 2^k的方形。当然,也不能让公主无限制的在那儿等,对吧?由于你使用的是计算机,所以实现时间为1s。

    输入格式
    输入文件共 2行。

    第一行:k,即给定被填补迷宫的大小为 2^k* 2^k(0<k≤10);

    第二行:x,y,即给出公主所在方格的坐标(x 为行坐标,y 为列坐标),x 和 y 之间有一个空格隔开。

    输出格式
    将迷宫填补完整的方案:每一行为x y c(x,y 为毯子拐角的行坐标和列坐标, c 为使用毯子的形状,具体见上面的图 1,毯子形状分别用 1,2,3,4 表示,x,y,c 之间用一个空格隔开)。

    输入输出样例
    输入 #1复制
    3
    3 3
    输出 #1复制
    5 5 1
    2 2 4
    1 1 4
    1 4 3
    4 1 2
    4 4 1
    2 7 3
    1 5 4
    1 8 3
    3 6 3
    4 8 1
    7 2 2
    5 1 4
    6 3 2
    8 1 2
    8 4 1
    7 7 1
    6 6 1
    5 8 3
    8 5 2
    8 8 1
    思路:分治思想。顾名思义,分而治之。这道题本蒟蒻做了好久好久~~~ 先看思路。
    先假设k=1,那么公主站立的位置只能有4种情况。而对于这四种情况,我们可以对应四种毯子来处理。
    看k=2的情况,我们先判断特殊点(即公主站立的位置),我们发现,无论公主站在什么位置,我们都可以将整个的大方格分成四个2*2的小方格。找到公主站立的位置,我们可以根据k=1时的情况来处理,那么剩下的三个方格怎么办呢?我们考虑,是不是可以给每个格子增加一个特殊点,来按照上面的方法处理呢?通过观察我们发现,这种做法是可以的。这样一来,整个问题就被分成了若干个小问题,交给计算机去递归就好了。
    那么k>2时的情况是怎么样的呢?通过画图,我们可以发发现,无论k是几,我们总能按照上面的思想将问题分成若干个小问题去处理。具体实现可以看代码。
    在这里插入图片描述

    import java.util.Scanner;
    
    public class Main {
        static int k,x0,y0;
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            k = sc.nextInt();
            x0= sc.nextInt();
            y0=sc.nextInt();
            dfs(x0,y0,1,1,1<<k);
        }
        public static void dfs(int x,int y,int zx,int zy,int len){
            //x y 为我们要处理的点的坐标 zx zy为我们处理的格子的左上角的坐标 len为我们当前处理格子的边长
            if(len==1){
                return ;
            }
            len/=2;
            if(x-zx<len&&y-zy<len){ //我们要处理的点位于当前格子的左上角
                System.out.println((zx+len)+" "+(zy+len)+" 1");//将大格子中间位置进行处理 进行分治
                dfs(x,y,zx,zy,len);//继续递归处理左上角
                dfs(zx+len-1,zy+len,zx,zy+len,len);//继续递归处理右上角
                dfs(zx+len,zy+len-1,zx+len,zy,len);//继续递归处理左下角
                dfs(zx+len,zy+len,zx+len,zy+len,len);//继续递归处理右下角
            }else if(x-zx<len&&y-zy>=len){//我们要处理的点位于当前格子的右上角
                System.out.println((zx+len)+" "+(zy+len-1)+" 2");
                dfs(zx+len-1,zy+len-1,zx,zy,len);
                dfs(x,y,zx,zy+len,len);
                dfs(zx+len,zy+len-1,zx+len,zy,len);
                dfs(zx+len,zy+len,zx+len,zy+len,len);
            }else if(x-zx>=len&&y-zy<len){//我们要处理的点位于当前格子的左下角
                System.out.println((zx+len-1)+" "+(zy+len)+" 3");
                dfs(zx+len-1,zy+len-1,zx,zy,len);
                dfs(zx+len-1,zy+len,zx,zy+len,len);
                dfs(x,y,zx+len,zy,len);
                dfs(zx+len,zy+len,zx+len,zy+len,len);
            }else{//我们要处理的点位于当前格子的右下角
                System.out.println(zx+len-1+" "+(zy+len-1)+" 4");
                dfs(zx+len-1,zy+len-1,zx,zy,len);
                dfs(zx+len-1,zy+len,zx,zy+len,len);
                dfs(zx+len,zy+len-1,zx+len,zy,len);
                dfs(x,y,zx+len,zy+len,len);
            }
        }
    }
    
    

    2 外星密码

    题目描述
    有了防护伞,并不能完全避免 2012 的灾难。地球防卫小队决定去求助外星种族的帮 助。经过很长时间的努力,小队终于收到了外星生命的回信。但是外星人发过来的却是一 串密码。只有解开密码,才能知道外星人给的准确回复。解开密码的第一道工序就是解压 缩密码,外星人对于连续的若干个相同的子串“X”会压缩为“[DX]”的形式(D 是一个整 数且 1≤D≤99),比如说字符串“CBCBCBCB”就压缩为“[4CB]”或者“[2[2CB]]”,类 似于后面这种压缩之后再压缩的称为二重压缩。如果是“[2[2[2CB]]]”则是三重的。现 在我们给你外星人发送的密码,请你对其进行解压缩。

    输入格式
    第一行:一个字符串

    输出格式
    第一行:一个字符串

    输入输出样例
    输入
    AC[3FUN]
    输出
    ACFUNFUNFUN
    说明/提示
    【数据范围】

    对于 50%的数据:解压后的字符串长度在 1000 以内,最多只有三重压缩。
    对于 100%的数据:解压后的字符串长度在 20000 以内,最多只有十重压缩。 对于 100%的数据:保证只包含数字、大写字母、’[‘和’]‘

    思路:看到这道题,不难想到应该递归着去做。我的思路是将字符串储存在数组中,然后根据每次遇到 ’[ ‘便进行解压,解压的次数我们很容易得出。进而只需要我们去遍历这个数组,当我们遇到’ ] ‘的时候返回遍历得到的字符串,然后乘以相应的循环次数即可。重点来了,我们遇到第一个’ ] '便返回了,那么如果第一个右括号右边还有需要解压的数据怎么办呢?想一下,括号成对出现,去掉了一个右括号必然去掉一个左括号。也就意味着这组待解压的数据我们已经解压完成了,我们继续解压后面的数据就好了。也就是说,在左括号的地方再加一次递归,用以获取后面的数据就好了。

    import java.util.Scanner;
    
    public class Main {
        static String str = "",ans="";
        static char[] ch;
        static int maxIndex=-1;
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            str = sc.next();
            ch = str.toCharArray();
            ans=fun(0);
            System.out.println(ans);
        }
        public static String fun(int index){
            maxIndex = Math.max(index,maxIndex);
            int k = 0;
            String s ="";
            if(index==ch.length){
                return "";
            }
            if(ch[index]>='0'&&ch[index]<='9'){
                k = ch[index]-'0';
                if((index+1)<ch.length&&ch[index+1]>='0'&&ch[index+1]<='9'){
                    k = k*10+(ch[index+1]-'0');
                    index++;
                }
                String t =fun(index+1);
                for(int i=0;i<k;i++){
                    s+=t;
                }
            }else if(ch[index]>='A'&&ch[index]<='Z'){
                s = ch[index]+fun(index+1);
            }else if(ch[index]=='['){
                s = fun(index+1);
                s+=fun(maxIndex+1);
            }else if(ch[index]==']'){
                return "";
            }
            return s;
        }
    }
    
    

    3 栈

    题目背景
    栈是计算机中经典的数据结构,简单的说,栈就是限制在一端进行插入删除操作的线性表。

    栈有两种最重要的操作,即 pop(从栈顶弹出一个元素)和 push(将一个元素进栈)。

    栈的重要性不言自明,任何一门数据结构的课程都会介绍栈。宁宁同学在复习栈的基本概念时,想到了一个书上没有讲过的问题,而他自己无法给出答案,所以需要你的帮忙。

    题目描述

    在这里插入图片描述

    宁宁考虑的是这样一个问题:一个操作数序列,1,2,…,n(图示为 1 到 3 的情况),栈 A 的深度大于 n。

    现在可以进行两种操作,

    将一个数,从操作数序列的头端移到栈的头端(对应数据结构栈的 push 操作)
    将一个数,从栈的头端移到输出序列的尾端(对应数据结构栈的 pop 操作)
    使用这两种操作,由一个操作数序列就可以得到一系列的输出序列,下图所示为由 1 2 3 生成序列 2 3 1 的过程。

    在这里插入图片描述

    (原始状态如上图所示)

    你的程序将对给定的 n,计算并输出由操作数序列 1,2,…,n 经过操作可能得到的输出序列的总数。

    输入格式
    输入文件只含一个整数 n(1≤n≤18)。

    输出格式
    输出文件只有一行,即可能输出序列的总数目。

    输入输出样例
    输入
    3
    输出
    5

    思路:看大佬们写的题解,说可以用卡特兰数做,然而 菜鸡的我并不知道什么是卡特兰数 ,这不重要。。。此题用记忆化搜索做,十分简单。也不需要用dp去推状态转移方程。我们用count数组储存队列中剩余x个数,栈中剩余y个数时的情况数,那么当x=1时,我们只能有一种情况(出栈)。当栈不为空时,我们可以选择出栈或者入栈。因为记忆化搜索,所以数组数大于0的时候return即可。

    import java.util.Scanner;
    
    public class Main {
        static long n,ans;
        static long[][] count;
    
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            n = sc.nextLong();
            count = new long[(int) (n+1)][(int) (n+1)];
            System.out.println(dfs(n,0));
        }
        public static long dfs(long x,int y){ //队列中剩余x个数 栈中剩余y个数时的情况数
            if(count[(int) x][y]!=0){
                return count[(int) x][y];
            }
            if(x==0) return 1; //栈中没有数 则只能有一种情况
            if(y>0) count[(int) x][y]+=dfs(x,y-1);
            count[(int) x][y]+=dfs(x-1,y+1);
            return count[(int) x][y];
        }
    }
    
    

    4 黑白棋子的移动

    题目描述
    有 2n 个棋子排成一行,开始为位置白子全部在左边,黑子全部在右边,如下图为 n=5 的情况:

    ○○○○○●●●●●

    移动棋子的规则是:每次必须同时移动相邻的两个棋子,颜色不限,可以左移也可以右移到空位上去,但不能调换两个棋子的左右位置。每次移动必须跳过若干个棋子(不能平移),要求最后能移成黑白相间的一行棋子。如 n=5 时,成为:

    ○●○●○●○●○●

    任务:编程打印出移动过程。

    输入格式
    一个整数 n。

    输出格式
    若干行,表示初始状态和每次移动的状态,用"o"表示白子,"*“表示黑子,”-"表示空行。

    输入输出样例
    输入
    7
    输出
    ooooooo*******--oooooo--******o*oooooo******--o*ooooo--*****o*o*ooooo*****--o*o*oooo--****o*o*o*oooo****--o*o*o*ooo--***o*o*o*o*ooo*o**--*o*o*o*o--*o**oo*o*o*o*o*o*o*--o*o*o*o*--o*o*o*o*o*o*o*

    说明/提示
    4≤n≤100

    思路:分治。通过观察样例,我们发现,当待排序棋子数量>4时,棋子仅需两步就可以完成排序,而排序完成以后,我们只需要继续处理n-1的情况。也就是说,整个问题可以划分为n更小的问题去解决。当待排序棋子数量>4时,棋子仅需两步就可以完成排序,而我们发现当剩余待排序棋子数量为2*4的时候,规律发生了变化。但是没关系,对于n=4的情特判一下就好了。

    import java.util.Scanner;
    
    public class Main {
        static int n;
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            n = sc.nextInt();
            fun(n);
        }
        public static void fun(int k){
            if(k==4){
                String s1 = "oooo****";
                String s2 = "ooo--***o*";
                String s3 = "ooo*o**--*";
                String s4 = "o--*o**oo*";
                String s5 = "o*o*o*--o*";
                String s6 = "--o*o*o*o*";
                s1+="--";
                for(int i=0;i<n-4;i++){
                    s1+="o*";
                    s2+="o*";
                    s3+="o*";
                    s4+="o*";
                    s5+="o*";
                    s6+="o*";
                }
                System.out.println(s1+"
    "+s2+"
    "+s3+"
    "+s4+"
    "+s5+"
    "+s6);
                return ;
            }else{
                String s1 ="";
                String s2 ="";
                for(int i=0;i<k;i++){
                    s1+="o";
                }
                for(int i=0;i<k;i++){
                    s1+="*";
                }
                for(int i=0;i<k-1;i++){
                    s2+="o";
                }
                s2+="--";
                for(int i=0;i<k-1;i++){
                    s2+="*";
                }
                s1+="--";
                for(int i=0;i<n-k;i++){
                    s1+="o*";
                    s2+="o*";
                }
                s2+="o*";
                System.out.println(s1+"
    "+s2);
                fun(k-1);
            }
    
        }
    }
    
    

    5 数的计算

    题目描述
    我们要求找出具有下列性质数的个数(包含输入的自然数n):

    先输入一个自然数n(n≤1000),然后对此自然数按照如下方法进行处理:

    不作任何处理;

    在它的左边加上一个自然数,但该自然数不能超过原数的一半;

    加上数后,继续按此规则进行处理,直到不能再加自然数为止.

    输入格式
    1个自然数n(n≤1000)

    输出格式
    1个整数,表示具有该性质数的个数。

    输入输出样例
    输入
    6
    输出
    6
    说明/提示
    满足条件的数为
    6,16,26,126,36,136

    思路:dfs爆搜,超时了。。需要用dp或者说递推的思想来做。首先,用爆搜出来的数据打印一下n取不同值时的结果来找一下规律。
    n=0, n=1时,答案是1
    n=2, ans=2; n=3,ans=2
    n=4,ans=4; n=5,ans=4
    n=6,ans=6; n=7,ans=6
    n=8,ans=10 n=9,ans=10
    n=10,ans=14…
    把数据打印出来以后,很明显答案存在规律。
    当n%2=1时,f[n]=f[n-1]。
    当n%2=0时,f[n] =f[n-1]+f[n/2]。
    至此,此题结束。本菜鸡除了爆搜啥也不会。
    其实此题还有一种做法,就是打表,利用我们写的dfs,把答案储存起来就好了。。

    ac代码:

    
    
    import java.util.Scanner;
    
    public class Main {
        static int n,ans=1;
        static int[] f;
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            n = sc.nextInt();
            f= new int[n+1];
            f[0]=1;
            f[1]=1;
            for(int i=2;i<=n;i++){
                if(i%2==0){
                    f[i] = f[i-1]+f[i/2];
                }else{
                    f[i] = f[i-1];
                }
            }
            System.out.println(f[n]);
        }
    
    }
    

    超时代码:

    
    import java.util.Scanner;
    
    public class Main {
        static int[] count;
        static int n,ans=1;
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            n = sc.nextInt();
            count = new int[n+1];
            dfs(n);
            System.out.println(ans);
        }
        public static void dfs(int x){
            for(int i=1;i<=x/2;i++){
                ans++;
                dfs(i);
            }
        }
    }
    

    6 幂次方

    题目描述
    任何一个正整数都可以用 2 的幂次方表示。例如 137=2^7 + 2 ^3+ 2^0。
    同时约定方次用括号来表示,即 a^b可表示为 a(b)。

    由此可知,137可表示为 2(7)+2(3)+2(0)
    进一步:
    7= 2^2 +2+2^0
    ( 2^1 用 2表示),并且 3=2+2^0。
    所以最后 137可表示为 2(2(2)+2+2(0))+2(2+2(0))+2(0)。
    又如 1315 最后可表示为 2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)。

    输入格式
    一行一个正整数 n。
    输出格式
    符合约定的 n的 0,2 表示(在表示中不能有空格)。
    输入输出样例
    输入
    1315
    输出
    2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)

    思路:模拟递归。将n分解为二进制,比如1010(十进制的10),此时遍历每个不为0的数位,假设此数位代表的数为2的k次方。根据题意可以看出,k>2的时候是可以继续递归分解的,而k<=2的时候不能继续分解了,当k=0||k=2的时候输出2(0)||2(2),而k=1的时候直接输出2就好。
    对于括号和加号要特别注意,何时输出括号,何时输出加号。

    
    import java.math.BigInteger;
    import java.util.Scanner;
    
    public class Main {
        static int n;
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            n = sc.nextInt();
            char[] ch = Integer.toBinaryString(n).toCharArray();
            ok(ch);
        }
        public static void ok(char[] ch){
            boolean flag = false;
            for(int i=0,t=ch.length-1;i<ch.length;i++,t--){
                if(ch[i]!='0'){
                    if(flag){
                        System.out.print("+");
                    }
                    flag = true;
                    if(t==0){
                        System.out.print("2(0)");
                    }else if(t==1){
                        System.out.print("2");
                    }else if(t==2){
                        System.out.print("2(2)");
                    }else{
                        System.out.print("2(");
                        char[] c1 = Integer.toBinaryString(t).toCharArray();
                        ok(c1);
                        System.out.print(")");
                    }
                }
            }
        }
    }
    
    

    7 小A点菜

    题目背景
    uim神犇拿到了uoi的ra(镭牌)后,立刻拉着基友小A到了一家……餐馆,很低端的那种。

    uim指着墙上的价目表(太低级了没有菜单),说:“随便点”。

    题目描述
    不过uim由于买了一些辅(e)辅(ro)书,口袋里只剩M元(M≤10000)。

    餐馆虽低端,但是菜品种类不少,有N种(N≤100),第i种卖ai元(ai ≤1000)。由于是很低端的餐馆,所以每种菜只有一份。

    小A奉行“不把钱吃光不罢休”,所以他点单一定刚好吧uim身上所有钱花完。他想知道有多少种点菜方法。

    由于小A肚子太饿,所以最多只能等待1秒。

    输入格式
    第一行是两个数字,表示N和M。

    第二行起N个正数ai(可以有相同的数字,每个数字均在1000以内)。

    输出格式
    一个正整数,表示点菜方案数,保证答案的范围在int之内。

    输入输出样例
    输入
    4 4
    1 1 2 2
    输出
    3

    思路:01背包求方案数。题目求的是恰好花完m元时的情况数,我们定义一个二维数组f,储存方案数。
    其中 f[i][j] 表示判断i件菜品恰好花完j元时的方案数
    1 :初始化
    f[i][0] = 1 表示有i件菜品恰好花完0元时只有一种方案
    f[0][j] = 0 表示有0件物品恰好花完j元时是不可能的 只能有0种方案 j!=0
    2: 状态转移方程
    if(j<a[i]) f[i][j] = f[i-1][j]
    if(j>=a[i]) f[i][j] = f[i-1][j]+f[i-1][j-a[i]]
    至此,思路此题就可以结束了。但是我们都知道,背包问题是可以在空间上进行优化的,我们可以把二维数组优化为一维数组。代码如下:
    空间优化后的代码:

    
    
    import java.util.Scanner;
    
    public class Main {
        static int n,m;
        static int[] a,ans;
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            n = sc.nextInt();
            m = sc.nextInt();
            a = new int[n+1];
            ans = new int[m+1];
            for(int i=1;i<=n;i++){
                a[i] = sc.nextInt();
            }
            ans[0] = 1;
            for(int i=1;i<=n;i++){
                for(int j=m;j>=a[i];j--){
                    ans[j] = ans[j]+ans[j-a[i]];
                }
            }
            System.out.print(ans[m]);
        }
    }
    

    没有优化的代码:

    
    
    import java.util.Scanner;
    
    public class Main {
        static int n,m;
        static int[] a;
        static int[][] f;
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            n = sc.nextInt();
            m = sc.nextInt();
            a = new int[n+1];
            f = new int[n+1][m+1];
            for(int i=1;i<=n;i++){
                a[i] = sc.nextInt();
            }
            for(int i=0;i<=n;i++){
                f[i][0] = 1;
            }
           for(int i=1;i<=n;i++){
                for(int j=1;j<=m;j++){
                    if(j<a[i])  f[i][j] = f[i-1][j];
                    if(j>=a[i])  f[i][j] = f[i-1][j]+f[i-1][j-a[i]];
                }
            }
            System.out.print(f[n][m]);
        }
    }
    

    8 南蛮图腾

    题目描述
    自从到了南蛮之地,孔明不仅把孟获收拾的服服帖帖,而且还发现了不少少数民族的智慧,他发现少数民族的图腾往往有着一种分形的效果,在得到了酋长的传授后,孔明掌握了不少绘图技术,但唯独不会画他们的图腾,于是他找上了你的爷爷的爷爷的爷爷的爷爷……帮忙,作为一个好孙子的孙子的孙子的孙子……你能做到吗?
    输入格式
    每个数据一个数字,表示图腾的大小(此大小非彼大小) n<=10
    输出格式
    这个大小的图腾
    输入输出样例
    输入
    2
    输出
    在这里插入图片描述

    输入
    3
    输出
    在这里插入图片描述
    思路:找一下规律,我们会发现,n = 2的情况是由n =1复制而来的。n=3的情况是由n=2复制而来的。。。蒟蒻不懂什么分治,暴力模拟就完了。。。另外,我觉得用字符串数组储存比用字符数组储存要简单的多,获得n以后,直接复制,注意空格!!!
    膜拜打表的大哥!

    import java.util.Scanner;
    
    public class Main {
        static int n;
        static String[] ans ;
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            n = sc.nextInt();
            ans = new String[(1<<n)+1];
            ans[1]="/\";
            ans[2]="/__\";
            for(int i=2;i<=n;i++){
                fun(i);
            }
            for(int i=1;i<=(1<<n);i++){
                for(int j=0;j<((1<<n)-i);j++){
                    System.out.print(" ");
                }
                System.out.println(ans[i]);
            }
        }
        public static void fun(int k){ //第k个图腾 需要复制高度1<<(k-1)
            int h0 = 1<<(k-1);
            int h1 = 1<<k;
            for(int i=h0+1;i<=h1;i++){
                ans[i] = ans[i-h0] ;
                int temp = (1<<k)-2-2*(i-(1<<(k-1))-1);
                for(int j=0;j<temp;j++){
                    ans[i]+=" ";
                }
                ans[i] +=ans[i-h0] ;
            }
        }
    }
    

    9 Function

    题目描述
    对于一个递归函数w(a,b,c)
    如果a≤0 or b≤0 or c≤0就返回值1.
    如果a>20 or b>20 or c>20就返回w(20,20,20)
    如果a<b并且b<c 就返回w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c)
    其它的情况就返回w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1)
    这是个简单的递归函数,但实现起来可能会有些问题。当a,b,c均为15时,调用的次数将非常的多。你要想个办法才行.

    absi2011 : 比如 w(30,−1,0)既满足条件1又满足条件2
    这种时候我们就按最上面的条件来算
    所以答案为1

    输入格式
    会有若干行。

    并以−1,−1,−1结束。

    保证输入的数在[-9223372036854775808,9223372036854775807]之间,并且是整数。

    输出格式
    输出若干行,每一行格式:

    w(a, b, c) = ans

    注意空格。

    输入输出样例
    输入
    1 1 1
    2 2 2
    -1 -1 -1
    输出
    w(1, 1, 1) = 2
    w(2, 2, 2) = 4
    思路:题目已经提示了,爆搜应该会超时,我们采用记忆化搜索,储存一下结果就好了。

    
    
    import java.util.Scanner;
    
    public class Main {
        static long a,b,c;
        static boolean[][][] flag = new boolean[25][25][25];
        static long[][][] book = new long[25][25][25];
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            while(true){
                a = sc.nextLong();
                b = sc.nextLong();
                c = sc.nextLong();
                if(a==-1&&b==-1&&c==-1){
                    break;
                }
                System.out.println("w("+a+", "+b+", "+c+") = "+fum(a,b,c));
            }
        }
        public static long fum(long q,long w,long e){
            if(q<=0||w<=0||e<=0){
                return 1;
            }else if(q>20||w>20||e>20){
               return fum(20,20,20);
            }
            if(flag[(int) q][(int) w][(int) e]){
                return book[(int) q][(int) w][(int) e];
            }
            if(q<w&&w<e){
                flag[(int) q][(int) w][(int) e]=true;
                book[(int) q][(int) w][(int) e] = fum(q,w,e-1)+fum(q,w-1,e-1)-fum(q,w-1,e);
                return book[(int) q][(int) w][(int) e];
            }else{
                flag[(int) q][(int) w][(int) e]=true;
                book[(int) q][(int) w][(int) e]= fum(q-1,w,e)+fum(q-1,w-1,e)+fum(q-1,w,e-1)-fum(q-1,w-1,e-1);
                return book[(int) q][(int) w][(int) e];
            }
        }
    }
    

    10 蜜蜂路线

    题目描述
    一只蜜蜂在下图所示的数字蜂房上爬动,已知它只能从标号小的蜂房爬到标号大的相邻蜂房,现在问你:蜜蜂从蜂房 m开始爬到蜂房 n,m<n,有多少种爬行路线?(备注:题面有误,右上角应为 n−1)

    在这里插入图片描述

    输入格式
    输入 m,n 的值

    输出格式
    爬行有多少种路线

    输入输出样例
    输入
    1 14
    输出
    377
    说明/提示
    对于100%的数据,M,N≤1000

    思路:dp,水题,注意一下高精度,没啥好说的。。。

    
    
    import java.math.BigInteger;
    import java.util.Scanner;
    
    public class Main {
        static int m,n,x0,y0,x1,y1,tx,ty;
        static BigInteger[][] dp = new BigInteger[510][510];
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            m = sc.nextInt();
            n = sc.nextInt();
            if(m%2==0){
                x0 = 1;
                y0 = m/2;
                dp[0][y0]=BigInteger.ZERO;
                dp[1][y0-1]=BigInteger.ZERO;
            }else{
                x0 = 0;
                y0 = m/2+1;
                dp[0][y0-1]=BigInteger.ZERO;
                dp[1][y0-1]=BigInteger.ZERO;
            }
            dp[x0][y0]=BigInteger.ONE;
            if(n%2==0){
                x1 = 1;
                y1 = n/2;
            }else{
                x1 = 0;
                y1 = n/2+1;
            }
            for(int i=m+1;i<=n;i++){
                if(i%2==0){
                    tx = 1;
                    ty= i/2;
                    dp[tx][ty]=dp[1][ty-1].add(dp[0][ty]);
                }else{
                    tx = 0;
                    ty= i/2+1;
                    dp[tx][ty]=dp[0][ty-1].add(dp[1][ty-1]);
                }
            }
            System.out.println(dp[x1][y1]);
        }
    }
    

    11 过河卒

    题目描述
    棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。

    棋盘用坐标表示,A 点 (0,0)、B 点 (n,m),同样马的位置坐标是需要给出的。

    在这里插入图片描述

    现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。

    输入格式
    一行四个正整数,分别表示 B 点坐标和马的坐标。

    输出格式
    一个整数,表示所有的路径条数。

    输入输出样例
    输入 #1复制
    6 6 3 3
    输出 #1复制
    6
    说明/提示
    对于 100% 的数据,1≤n,m≤20,0≤ 马的坐标≤20。

    思路 :记忆化搜索或者dp都能过。
    dp:

    
    import java.util.Scanner;
    
    public class Main {
        static int n,m,x1,y1;
        static long[][] dp;
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            n = sc.nextInt();
            m = sc.nextInt();
            x1= sc.nextInt();
            y1= sc.nextInt();
            dp = new long[n+1][m+1];
            dp[0][0] = 1;
            for(int i = 1;i<=n;i++){
                if(ok(i,0)){
                    dp[i][0]=dp[i-1][0];
                }
            }
            for(int j=1;j<=m;j++){
                if(ok(0,j)){
                    dp[0][j]=dp[0][j-1];
                }
            }
            for(int i=0;i<=n;i++){
                for(int j=0;j<=m;j++){
                    if(dp[i][j]!=0){
                        continue;
                    }else if(ok(i,j)){
                        int tx = i-1;
                        int ty = j;
                        if(tx>=0&&tx<=n&&ty>=0&&ty<=m&&ok(tx,ty)){
                            dp[i][j]+=dp[tx][ty];
                        }
                        tx = i;
                        ty = j-1;
                        if(tx>=0&&tx<=n&&ty>=0&&ty<=m&&ok(tx,ty)){
                            dp[i][j]+=dp[tx][ty];
                        }
                    }
                }
            }
            System.out.println(dp[n][m]);
        }
        public static boolean ok(int x,int y){
            if((x==x1&&y==y1)||(x==x1-1&&y==y1-2)||(x==x1-2&&y==y1-1)||(x==x1-2&&y==y1+1)||(x==x1-1&&y==y1+2)||(x==x1+1&&y==y1-2)||(x==x1+2&&y==y1-1)||(x==x1+2&&y==y1+1)||(x==x1+1&&y==y1+2)){
                return false;
            }
            return true;
        }
    }
    

    记忆化搜索:

    
    
    import java.math.BigInteger;
    import java.util.Scanner;
    
    
    public class Main {
        static int n,m,x1,y1;
        static long[][] dp;
        public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            n = sc.nextInt();
            m = sc.nextInt();
            x1= sc.nextInt();
            y1= sc.nextInt();
            dp = new long[n+1][m+1];
            dp[n][m] = 1;
            dfs(0,0);
            System.out.println(dp[0][0]);
        }
        public static boolean ok(int x,int y){
            if((x==x1&&y==y1)||(x==x1-1&&y==y1-2)||(x==x1-2&&y==y1-1)||(x==x1-2&&y==y1+1)||(x==x1-1&&y==y1+2)||(x==x1+1&&y==y1-2)||(x==x1+2&&y==y1-1)||(x==x1+2&&y==y1+1)||(x==x1+1&&y==y1+2)){
                return false;
            }
            return true;
        }
        public static long dfs(int x,int y){
            if(dp[x][y]!=0){
                return dp[x][y];
            }
            int tx;
            int ty;
            tx = x+1;
            ty = y;
            if(tx>=0&&tx<=n&&ty>=0&&ty<=m&&ok(tx,ty)){
                dp[x][y]+=dfs(tx,ty);
            }
            tx = x;
            ty = y+1;
            if(tx>=0&&tx<=n&&ty>=0&&ty<=m&&ok(tx,ty)){
                dp[x][y]+=dfs(tx,ty);
            }
            return dp[x][y];
        }
    
    }
    

    未完待续。。。。。。

  • 相关阅读:
    转载: HashMap的工作原理
    什么web服务器、什么是应用服务器,常见的web服务器和应用服务器有哪些
    论文查重应对策略
    web测试方法总结
    软件测试中十大负面测试用例
    mysql 学习笔记
    Tomcat转jboss踩的那些坑
    实现简单的List功能
    java ScriptEngine 使用 (java运行脚本文件)
    rmi 工作原理
  • 原文地址:https://www.cnblogs.com/fxzemmm/p/14847932.html
Copyright © 2011-2022 走看看