刷题中遇到的记忆化方法

刷题中遇到的记忆化方法

记忆化搜索需要解决的一个重要问题就是如何对状态进行编辑

我能赢吗

在 “100 game” 这个游戏中,两名玩家轮流选择从 1 到 10 的任意整数,累计整数和,先使得累计整数和达到或超过 100 的玩家,即为胜者。

如果我们将游戏规则改为 “玩家不能重复使用整数” 呢?

例如,两个玩家可以轮流从公共整数池中抽取从 1 到 15 的整数(不放回),直到累计整数和 >= 100。

给定一个整数 maxChoosableInteger (整数池中可选择的最大数)和另一个整数 desiredTotal(累计和),判断先出手的玩家是否能稳赢(假设两位玩家游戏时都表现最佳)?

你可以假设 maxChoosableInteger 不会大于 20, desiredTotal 不会大于 300。

输入:
maxChoosableInteger = 10
desiredTotal = 11

输出:
false

解释:
无论第一个玩家选择哪个整数,他都会失败。
第一个玩家可以选择从 1 到 10 的整数。
如果第一个玩家选择 1,那么第二个玩家只能选择从 2 到 10 的整数。
第二个玩家可以通过选择整数 10(那么累积和为 11 >= desiredTotal),从而取得胜利.
同样地,第一个玩家选择任意其他整数,第二个玩家都会赢。

    //弄了半天理解错题了,这个指的是那个使得总累计和最先达标的玩家,运气
    //这题问的是稳赢而不是可以达到的最大数值,意味着需要将所有的状态遍历一遍,抽取出是不是赢这个信息
    //而不是记录最大数值
    public int desire;
    public int[] maxcur;
    public int maxcou;
    public void dfs(int cur,int curnum){
        //记忆数组的意思可以理解为,在这个局面下,还剩这些num的时候是否能稳赢
        //稳赢的意思可以理解为,对方在这种情况下无论怎么拿都会输
        int tt1=-1;
        if(maxcur[cur]!=0){
            return ;
        }else {
            for (int i = 0; i < maxcou; i++) {
                if ((cur & 1 << i) != 0) {
                    continue;
                } else {
                    int tem = cur | (1 << i);
                    if(curnum-i-1<=0){
                        //在本次已经胜了
                        maxcur[cur]=1;
                        return ;//在这个唯一的局面下,只有赢这一种选择,但是上一层循环不应该在这终止
                    }
                    dfs(tem,curnum-i-1);
                    int a=maxcur[tem];
                    if(a==-1){
                        tt1=1;
                    }
                }
            }
        }
        maxcur[cur]=tt1;
        return;
    }

    public boolean canIWin(int maxChoosableInteger, int desiredTotal) {
        //最多只有20个数字设置记忆化数组进行记忆化搜索
        //设计记忆化数据结构maxcur,这个数据结构的意思可以理解为,当前剩余数据的情况,做出选择可以得到的最好效果
        if (maxChoosableInteger >= desiredTotal) {
            return true;
        }
        //第二种特殊情况,所有元素的和都小于预期值,则永远无法赢
        if (maxChoosableInteger * (maxChoosableInteger + 1) / 2 < desiredTotal) {
            return false;
        }
         this.maxcur=new int[(1<<maxChoosableInteger)];
         this.desire=desiredTotal;
         this.maxcou=maxChoosableInteger;
         dfs(0,desiredTotal);
         if(maxcur[0]==1){
             return true;
         }
         return(false);
    }

本题的几个要点

  1. 理解博弈问题的意思:都发挥出最好状态->可以理解为回溯到本部,所有情况中最好的,如果有希望则一定能赢。
  2. 位运算,考虑清楚i循环代表的什么内容,例如,本题中的i代表的其实是本书-1
  3. 对于特殊情况要分开考虑

矩阵中的最长递增路径

给定一个整数矩阵,找出最长递增路径的长度。

对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)

输入: nums = 
[
  [9,9,4],
  [6,6,8],
  [2,1,1]
] 
输出: 4 
解释: 最长递增路径为 [1, 2, 6, 9]。

这题没啥好说的,就记录一个数组,数组的意思是,以本数开头所能达到的最长序列即可,由于序列中的数是一个个递减的,不用怕有循环的情况。

    public int[][] mem;
    public int[][] mat;
    public boolean inrange(int a,int b,int x,int y){
        return 0<=x&&x<mem.length&&0<=y&&y<mem[0].length&&mat[a][b]<mat[x][y];
    }
    public int dfs(int x,int y){
        if(mem[x][y]!=0){
            return mem[x][y];
        }
        int max=1;
        if(inrange(x,y,x+1,y)){
            max=Math.max(max,dfs(x+1,y)+1);
        }
        if(inrange(x,y,x-1,y)){
            max=Math.max(max,dfs(x-1,y)+1);
        }
        if(inrange(x,y,x,y+1)){
            max=Math.max(max,dfs(x,y+1)+1);
        }
        if(inrange(x,y,x,y-1)){
            max=Math.max(max,dfs(x,y-1)+1);
        }
        mem[x][y]=max;
        return max;
    }
    public int longestIncreasingPath(int[][] matrix) {
        if(matrix.length==0||matrix[0].length==0){
            return 0;
        }
        this.mem=new int[matrix.length][matrix[0].length];
        this.mat=matrix;
        int fin=1;
        for(int i=0;i< mem.length;i++){
            for(int j=0;j<mem[0].length;j++){
                //这里写错了
                fin=Math.max(fin,dfs(i,j));
            }
        }
        return fin;
    }

1411. 给 N x 3 网格图涂色的方案数

记忆化方法,虽然很慢,但在考场上确实只能想清楚此种方法了

class Solution {
    long[][][][] dp;
    int n;
    long mod=1000000007;
    public long dfs(int color1,int color2,int color3,int dep){
        long fin=0;

        if(dep==n){
            dp[color1][color2][color3][dep]=1;
            return 1;
        }
        if(dp[color1][color2][color3][dep]!=0){
            return dp[color1][color2][color3][dep];
        }
        for(int i=1;i<=3;i++){
            for(int j=1;j<=3;j++){
                for(int k=1;k<=3;k++){
                    if(i==j||j==k||color1==i||color2==j||color3==k){
                        continue;
                    }
                    fin=fin+(dfs(i,j,k,dep+1))%mod;

                }
            }
        }
        dp[color1][color2][color3][dep]=fin%mod;
        return(fin%mod);

    }
    public int numOfWays(int n) {
        this.n=n;
        this.dp=new long[4][4][4][n+1];
        return (int)(dfs(0,0,0,0));
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值