19.2:纸牌问题

news2024/11/18 21:36:52

给定一个整型数组arr,代表数值不同的纸牌排成一条线
玩家A和玩家B依次拿走每张纸牌
规定玩家A先拿,玩家B后拿
但是每个玩家每次只能拿走最左或最右的纸牌
玩家A和玩家B都绝顶聪明
请返回最后获胜者的分数

方法一:暴力解法

自然智慧。

package algorithmbasic.class19;

public class CardsInLine {

    public static int win2(int[] arr) {

        if (arr == null || arr.length < 2) {
            return -1;
        }

        int fresult = f2(arr, 0, arr.length - 1);

        int gresult = g2(arr, 0, arr.length - 1);

        return Math.max(fresult, gresult);
    }

    //作为先手函数,要在arr[L......R]范围上返回最好的成绩。
    public static int f2(int[] arr, int L, int R) {
        //如果现在只剩下一张纸牌了,作为先手直接拿走即可。
        if (L == R) {
            return arr[L];
        }
        //不止一张纸牌

        //如果我将最左侧的纸牌拿走,那接下来后手会怎么拿呢?然后就把自己当作后手来分析一下后手会如何做。
        int p1 = arr[L] + g2(arr, L + 1, R);

        //如果我将最右侧的纸牌拿走,那接下来后手会怎么拿呢?然后就把自己当作后手来分析一下后手会如何做。
        int p2 = arr[R] + g2(arr, L, R - 1);

        return Math.max(p1, p2);
    }

    private static int g2(int[] arr, int L, int R) {

        if (L == R) {
            return 0;
        }

        // 后手拿走最左侧的数,然后判断先手会在剩下的【L + 1, R】范围上的最优解。
        int p1 = f2(arr, L + 1, R);

        // 后手拿走最右侧的数,然后判断先手会在剩下的【L , R - 1】范围上的最优解。
        int p2 = f2(arr, L, R - 1);

        // p1 -> 先手在的【L + 1, R】范围上的最优解。
        // p2 -> 先手在的【L, R - 1】范围上的最优解。

        // 因为先手与后手都绝顶的聪明,所以最优解一定会被先手拿走。后手只能返回两者中最小的结果。

        //后手只能返回一个最小的,因为大的那个已经被先手拿走了。
        return Math.min(p1, p2);


    }

    // 给定数组arr之后,绝顶聪明的先手与后手的结果其实早已决定无力回天。 ----> 命运的安排
    // 于此同时主动权一直在先手这方。

}

方法二:记忆化搜索优化

暴力函数

	public static int win1(int[] arr) {

        int f = f1(arr, 0, arr.length - 1);

        int l = g1(arr, 0, arr.length - 1);

        return Math.max(f, l);
    }

    //返回先手在arr[L......R]范围上的最优分数。
    public static int f1(int[] arr, int L, int R) {

        if (L == R) {
            return arr[L];
        }

        int p1 = arr[L] + g1(arr, L + 1, R);

        int p2 = arr[R] + g1(arr, L, R - 1);

        return Math.max(p1, p2);
    }

    private static int g1(int[] arr, int L, int R) {

        if (L == R) {
            return 0;
        }

        int p1 = f1(arr, L + 1, R);

        int p2 = f1(arr, L, R - 1);

        return Math.min(p1, p2);
    }

1:分析原函数

在这里插入图片描述

发现加入缓存是合理的,因为真的出现重复计算了。

2:确定建立几张表以及表的范围

因为g方法与f方法都可能存在重复,所以建立两张表,表的范围是 [N] [N]

3:将表加入到原函数中。

// 方法二:记忆化搜索。
private static int win2(int[] arr) {

    int N = arr.length;

    int[][] fmap = new int[N][N];

    int[][] gmap = new int[N][N];

    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            fmap[i][j] = -1;
            gmap[i][j] = -1;
        }
    }

    int f = f2(arr, 0, arr.length - 1, fmap, gmap);

    int l = g2(arr, 0, arr.length - 1, fmap, gmap);

    return Math.max(f, l);
}

public static int f2(int[] arr, int L, int R, int[][] fmap, int[][] gmap) {

    if (fmap[L][R] != -1) {
        return fmap[L][R];
    }

    int ans = -1;

    if (L == R) {

        ans = arr[L];
    } else {

        int p1 = arr[L] + g2(arr, L + 1, R, fmap, gmap);

        int p2 = arr[R] + g2(arr, L, R - 1, fmap, gmap);

        ans = Math.max(p1, p2);
    }

    fmap[L][R] = ans;
    return ans;
}

private static int g2(int[] arr, int L, int R, int[][] fmap, int[][] gmap) {

    if (gmap[L][R] != -1) {
        return gmap[L][R];
    }
    int ans = -1;

    if (L == R) {
        ans = 0;
    } else {
        int p1 = f2(arr, L + 1, R, fmap, gmap);

        int p2 = f2(arr, L, R - 1, fmap, gmap);

        ans = Math.min(p1, p2);
    }

    gmap[L][R] = ans;

    return ans;
}

方法三:依赖版本迭代实现

1:分析依赖关系,确定表的大小

**分析依赖关系:**f 函数依赖g 函数,g 函数还会依赖 f 函数。

–> f 函数与g 函数中可能都存在重复值 --> 建两张表

确定表的大小关系:

public static int f1(int[] arr, int L, int R) {。。。。}
 private static int g1(int[] arr, int L, int R) {。。。。}

L的范围与R的范围不会超过数组的长度。

所以两张表的大小可以都是arr[arr.length] [arr.length]

2:根据原函数从上到下开始逐一分析

在这里插入图片描述

首先分析一下结果想要什么

	public static int win1(int[] arr) {

        int f = f1(arr, 0, arr.length - 1);

        int l = g1(arr, 0, arr.length - 1);

        return Math.max(f, l);
    }

发现结果想要的是f1图中的 (0, n-1 )位置。

以及g1图中的 (0, n-1 )位置。

分析f1方法

	public static int f1(int[] arr, int L, int R) {

        if (L == R) {
            return arr[L];
        }

        int p1 = arr[L] + g1(arr, L + 1, R);

        int p2 = arr[R] + g1(arr, L, R - 1);

        return Math.max(p1, p2);
    }

当L == R的时候,返回的是arr [L] - -> 直接在表中写出来。

当L != R时,即L < R时,会依赖 g1 方法。会依赖方法的那个位置呢?依赖的位置是:在f1图中的位置对应到g1图中的位置,

依赖的是在g1图中位置的左侧与下侧位置。与此同时,g1方法的依赖关系与f1一样。

3:根据原函数开始填表

原始函数

	public static int win1(int[] arr) {

        int f = f1(arr, 0, arr.length - 1);

        int l = g1(arr, 0, arr.length - 1);

        return Math.max(f, l);
    }

    //返回先手在arr[L......R]范围上的最优分数。
    public static int f1(int[] arr, int L, int R) {

        if (L == R) {
            return arr[L];
        }

        int p1 = arr[L] + g1(arr, L + 1, R);

        int p2 = arr[R] + g1(arr, L, R - 1);

        return Math.max(p1, p2);
    }

    private static int g1(int[] arr, int L, int R) {

        if (L == R) {
            return 0;
        }

        int p1 = f1(arr, L + 1, R);

        int p2 = f1(arr, L, R - 1);

        return Math.min(p1, p2);
    }

填表的最终结果如下:

private static int win3(int[] arr) {

        int N = arr.length;

        int[][] fmap = new int[N][N];

        int[][] gmap = new int[N][N];

        //根据f1中if (L == R) {return arr[L]; } 以及g1中的第一句话。
        for (int col = 0; col < N; col++) {
            fmap[col][col] = arr[col];
            gmap[col][col] = 0;
        }

        for (int start = 1; start < N; start++) {

            //斜着填
            int row = 0;
            int col = start;

            while(col < N) {
                fmap[row][col] = Math.max(arr[row] + gmap[row][col - 1], 
                                          arr[row] + gmap[row + 1][col]);
                gmap[row][col] = Math.min(fmap[row][col - 1], fmap[row + 1][col]);
                row++;
                col++;
            }
        }
        return Math.max(fmap[0][N - 1], gmap[0][N - 1]);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/799678.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【NLP入门教程】二十三、循环神经网络(RNN)| 小白专场

本教程旨在为初学者提供一份全面而系统的NLP入门指南&#xff0c;探索NLP的核心概念、方法和技术。无论你是计算机科学的新手&#xff0c;还是对自然语言处理领域感兴趣的研究人员&#xff0c;本教程都将为你提供所需的基础知识和实用技能。 专栏地址&#xff1a;&#x1f4d5…

【蓝图】p40-p43对象引用、变量有效性、实现键盘控制物体自转、简单点名系统

p40-p43对象引用、变量有效性、实现键盘控制物体自转、简单点名系统 p40对象引用、变量有效性p41实现键盘控制物体自转创建bool值控制旋转实现通过键盘控制自转 p42p43简单点名系统Get All Actors Of Class&#xff08;获得场景中所有该类的actor演员&#xff09;getFor Each L…

TEE GP(Global Platform)安全认证产品

TEE之GP(Global Platform)认证汇总 一、安全认证产品介绍 选择SECURITY和TEE SECURITY&#xff0c;然后SEARCH&#xff0c;可以看到TEE对应的安全认证产品。 二、HUAWEI ITRUSTEE V3.0 ON KIRIN 980 三、SAMSUNG TEEGRIS V4.1 参考&#xff1a; GlobalPlatform Certification…

【linux】暗夜精灵9自动升级BIOS后,无法进入双系统

1、问题描述 暗夜精灵9自动升级BIOS后,无法进入双系统,甚至没有“多重引导菜单” 2、解决方法 1)进入BIOS 惠普暗夜精灵9进入BIOS的按键是F10 2)设置启动项 需要设置三处 功能键延时:5秒 安全启动模式:关闭 UEFI模式下的开机顺序:ubuntu3)设置显卡切换 如果NVIDIA…

TCL(Tool Command Language)学习(三)-----字符串操作

一、比较 &#xff1a; string compare 格式 &#xff1a; string compare ?-nocase? ?-length int? string1 string2 把字符串 string1 和字符串 string2 进行比较 &#xff0c;返回值为 -1、0或 1 &#xff0c;分别对应 string1 小于、等于或大于string2。如果有 -len…

全球百年品牌,最高已改变25次

摘要&#xff1a;今天&#xff0c;我们看到的那些持久的公司并不是真正持续了100年的公司。在100年的时间里,他们改变了25次、5次或4次,如果不改变,他们就无法生存。 近期市场调研中&#xff0c;许战海咨询发现国内市场已经进入无限内卷的竞争阶段。如何在内卷的竞争中保持可持…

Android OpenGL ES Camera2 实现实时抠图效果

前面文章人像抠图 + OpenGL ES 还能这样玩?没想到吧,我们介绍了利用人像抠图算法生成的 mask 图,然后结合 OpenGL 可以产生一些有趣的效果。 抠图技术应用很广泛,比如很多手机的相机自带“人像留色”滤镜:人体区域保留彩色,人体区域之外灰度化。所以人像留色的关键技术在…

Unity下如何实现低延迟的全景RTMP|RTSP流渲染

技术背景 Unity3D可以用于创建各种类型的的应用程序&#xff0c;包括虚拟现实、培训模拟器等。以下是一些可以使用Unity3D全景播放的场景&#xff1a; 虚拟现实体验&#xff1a;全景视频可以用来创建逼真的虚拟环境&#xff0c;使用户能够感受到身临其境的感觉&#xff1b;培…

Spring中的Bean配置

bean配置 基础配置 别名配置 作用范围配置 1、为什么bean会默认为单例&#xff1f; 如果不为单例&#xff0c;则每一次调用bean&#xff0c;Spring的bean容器中&#xff0c;会出现多个该类的对象&#xff0c;这会造成容器的冗余。 2、适合交给容器进行管理的bean 表现层对象…

Ansys Zemax | 确保自由曲面设计的可制造性

本文专门介绍使用单点金刚石车床加工自由曲面的主要可制造性参数&#xff0c;解释了可制造性参数如何与仪器参数相关联&#xff0c;并展示了如何在 OpticStudio 中检查和控制这些可制造性参数。此外&#xff0c;还解释了如何处理其考察区域外的自由曲面的行为。例如&#xff0c…

MYSQL练习四

练习四 某公司的mis数据库中有部⻔表&#xff08;dept&#xff09;和员工表&#xff08;employee&#xff09;表结构及其数据如下&#xff1a; 表结构 dept表结构 employee表结构 表数据 dept表数据 employee表数据 完成以下SQL编写&#xff1a; 编写脚本&#xff0c;创建…

Eureka注册中心 与 OpenFeign调用接口

需求 一个应用通过接口&#xff0c;调用另一个应用的接口。使用OpenFeign来实现接口调用。 说明 通过OpenFeign&#xff08;本文接下来简称Feign&#xff09;调用远程接口&#xff0c;需要Eureka注册中心的支持。 OpenFeign调用接口的逻辑如下&#xff1a; 提供接口的应用…

617. 合并二叉树

题目 题解一&#xff1a;递归 /*** 递归* param root1* param root2* return*/public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {//结束条件if (root1 null) {return root2;} //结束条件if (root2 null) {return root1;}//两节点数值相加TreeNode me…

C++模拟实现stack

1.前言 stack 遵循的原则是先进后出&#xff0c;那到底是用list 还是 vector呢&#xff1f;其实都可以&#xff0c;但是队列queue就不一样了&#xff0c;他甚至不可以支付vector&#xff0c;因为效率太低了。 但是库里面用了一种新的类型&#xff0c;deque&#xff0c;它的实现…

【数据结构】实验五:栈

实验五 栈 一、实验目的与要求 1&#xff09;熟悉栈的类型定义和基本操作&#xff1b; 2&#xff09;灵活应用栈解决具体应用问题。 二、实验内容 1、判断回文数&#xff0c;回文是指正读反读均相同的字符序列&#xff0c;如“1221”和“12321”均是回文&#xff0c;但“…

揭秘全球最危险的11大网络间谍组织

根据安全研究人员的说法&#xff0c;下述这些都是全球最臭名昭著的&#xff0c;并且由民族国家资助的组织。 几十年前&#xff0c;当黑客入侵刚刚出现的时候&#xff0c;其大多是网络“发烧友”的“杰作”&#xff0c;他们痴迷于学习有关计算机和网络的一切知识。现如今&#x…

2023年Q2京东黑电行业热门类目数据分析报告(京东大数据)

随着家电行业的日趋成熟&#xff0c;黑电市场也愈加繁荣&#xff0c;黑色家电包括电视、音响、家庭影院、摄像机、游戏机等等带给人们娱乐的电器。 目前来看电视机在黑电行业的占有份额较大&#xff0c;而前面我们已经分析过电视市场&#xff0c;接下来我们继续一起来分析除电视…

实现PC端微信扫码native支付功能

目录 实现PC端微信扫码 简介 实现步骤 1. 获取商户号 2. 生成支付二维码 3. 监听支付结果 4. 发起支付请求 5. 处理支付回调 示例代码 结论 Native支付 Native支付的工作原理 Native支付的优势 Native支付的应用和市场地位 开通使用微信 native 支付流程 步骤一…

LaTeX Error: File `tabu.sty‘ not found.

虽然Miktex可以自动下载和安装没有的库&#xff0c;但有些时候也有可能会直接报错&#xff1a; LaTeX Error: File tabu.sty not found. 解决方法就是打开控制台。然后手动下载&#xff1a;

Sharding-JDBC分布式事务详解与实战

&#x1f680; ShardingSphere &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&…