4.17 训练周记

这周主要练习了记忆化搜索和基础数据结构。

这次的acm社团面试问了个很有意思的问题,就是bfs能否用栈实现。这个问题刚听到的时候挺懵的,不懂他到底想问啥。面试后好好思考了一下,这个答案是肯定的。bfs的本质就是从一个父状态衍生出多个合法的子状态,并不断判断每一种状态并继续产生每个子状态的子状态...

而我们为什么要用队列实现呢?因为队列按先进先出的特性先处理完更早压入队列的状态,一层一层的进行搜索,直到搜索 终点,此时的步数就是最短步数。用队列实现我们的逻辑和思路会非常清晰,但是如果只是想实现处理状态,和产生子状态直到搜索到目标的话,我认为只需要一个容器就能实现。虽然这个问题的结果可能没什么意义,但是思考这个问题的过程还是让我对搜索的认识更深入了一些。

1. 摆花

这题的朴素搜索思路比较简单,

1.搜索方向

2.搜索出口

首先考虑搜索方向,朴素的搜索我们可以考虑搜索每种花摆的个数,并加入sum。

然后是搜索的出口,只需要sum等于目标数m,方案数ans++,回溯,如果sum超过的目标数直接返回。


import java.math.BigInteger;
import java.util.HashMap;
import java.util.Scanner;

public class Main {
    static public int sum=0;
    static public int m,n;
    static  int [] arr=new int[1000];
    static int []dp=new int[1000];
    public static void main(String[] args) {
       Scanner in=new Scanner(System.in);
       n=in.nextInt();
        m=in.nextInt();
      for(int j=1;j<=n;j++ ){
          arr[j]=in.nextInt();
      }
        dfs(0,1);
        System.out.println(sum);
    }
    public static void dfs(int now,int dep){
            if(now>m)
                return;
            if(now==m){
                sum++;
                return;
            }
            if(dep>n)
                return ;
           for(int j=0;j<=arr[dep];j++){
               dfs(j+now,dep+1);
           }
    }
}

 如果采用记忆优化,搜索方向和出口其实并没有改变,但是效率高了很多



import java.math.BigInteger;
import java.util.HashMap;
import java.util.Scanner;

public class Main {

    static public int m,n;
    static int mod=(1000007);
    static  int [] arr=new int[1000];
    static int [][]dp=new int[1000][1000];
    public static void main(String[] args) {
       Scanner in=new Scanner(System.in);
       n=in.nextInt();
        m=in.nextInt();

      for(int j=1;j<=n;j++ ){
          arr[j]=in.nextInt();
      }
        System.out.println(dfs1(0,1));
    }

    public static int dfs1(int now,int dep){

        if(now==m)
            return 1;
        if(now>m)
            return 0;
        if(dep>n)
            return 0;
        if(dp[now][dep]!=0){
            return dp[now][dep];
        }
        int t=0;
        for(int j=0;j<=arr[dep];j++){
                t=(t+dfs1(now+j,dep+1))%mod;
        }
        dp[now][dep]=(t%mod);
        return t;

    }
}

2.编辑距离 - 洛谷

 

所以设dp[j][i]表示长度为j的a串要变成长度i的b串需要的最少步骤。也就是说此时的a子串完全等于此时的b子串

 但是我们现在只去讨论记忆化搜索不讨论状态转移咋得出的,这题让我学到了什么。

首先记忆化搜索本质也就是搜索,既然是搜索,我们就要明确两大点

1.搜索方向

2.搜索出口

首先是搜索方向,这题其实比较复杂。我们去分析每种操作对下一个搜索状态的影响

首先我们可以不进行操作,但是不操作又要保证a与b串匹配,只能当前a的字符与b的字符相同。才能匹配。所以当a[j]==a[i]时即可不进行操作,此时的操作数继承之前的 dp[j-1][i-1]

1.删除一个字符,首先,如果a串当前字符和b串当前字符相同,就没必要执行这一步。如果要删显然必须删除当前不同的a串字符。删除了当前字符,那么此时的状态则有删除之后的子串j-1得到。此时则需要搜索dfs(j-1,i)

2.插入一个字符,如果要在不匹配出插入一个字符,首先此字符肯定是与b串当前位置相同的字符。那么相当于a串的匹配不需要考虑b的这个字符,因为我能直接添加一个与他匹配,转而搜索dfs(j,i-1)

3.替换一个字符,这个操作相对好理解一些,a串当前位置的字符不匹配,则替换该字符为b的字符达到匹配的效果。此时,a和b都不再需要考虑这个字符了,因为我能进行一次操作完成这个字符的匹配,所以转而搜索dfs(j-1,i-1)

这样搜索方向就出来了。

接下来考虑搜索的出口,也就是搜索何时结束。

很显然,当我们从原串向下搜索子串的时候,当搜索的其中一个子串为空,开始返回。

而且因为需要记忆化,所以搜索到其中一个记忆数组已经计算过的时候也能直接返回

最后还有注意搜索的边界,如果字符串从0开始,那么向下搜索判断空串的时候,下标应为-1,而记忆数组的下标不能为负数,所以字符串的下标位置需要整体加一。

代码:(ps:java写算法题好难受)

import java.math.BigInteger;
import java.util.HashMap;
import java.util.Scanner;
public class Main {
    public static String a;
    public static String b;
    public static int[][] dp = new int[2001][2001];

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        a = in.next();
        b = in.next();
        for(int j=0;j<=a.length();j++){
            for(int i=0;i<=b.length();i++)
                dp[j][i]=-1;
        }
        System.out.println(dfs(a.length(),b.length()));
    }
    public static int dfs(int l1,int l2){
        if(dp[l1][l2]!=-1){
            return dp[l1][l2];
        }
        if(l1==0){
            return dp[l1][l2]=l2;
        }
        if(l2==0){
            return dp[l1][l2]=l1;
        }

        if(a.charAt(l1-1)==b.charAt(l2-1)){
            return dp[l1][l2]=dfs(l1-1,l2-1);
        }

        int n=99999999;
        for(int j=1;j<=3;j++){
            if(j==1){
                n=Math.min(dfs(l1-1,l2)+1,n);
            }
            else if(j==2){
                n=Math.min(dfs(l1,l2-1)+1,n);
            }
            else
                n=Math.min(dfs(l1-1,l2-1)+1,n);
        }
        dp[l1][l2]=n;
        return n;

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值