19.2:纸牌问题

给定一个整型数组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]);
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis 基础知识和核心概念解析:探索 Redis 的数据结构与存储方式

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

基于解析法和遗传算法相结合的配电网多台分布式电源降损配置(Matlab实现)

目录 1 概述 2 数学模型 2.1 问题表述 2.2 DG的最佳位置和容量&#xff08;解析法&#xff09; 2.3 使用 GA 进行最佳功率因数确定和 DG 分配 3 仿真结果与讨论 3.1 33 节点测试配电系统的仿真 3.2 69 节点测试配电系统仿真 4 结论 1 概述 为了使系统网损达到最低值&a…

1200*B. Vanya and Lanterns

Examples input 7 15 15 5 3 7 9 14 0 output 2.5000000000 input 2 5 2 5 output 2.0000000000 解析&#xff1a; 最大距离即为每相邻两盏灯之间的最大距离/2 注意起点没有灯&#xff0c;终点可能有灯&#xff0c;需要分别判断 #include<bits/stdc.h> using nam…

Cesium态势标绘专题-直线箭头(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

大语言模型LLM

目录 一、语言模型的发展 语言模型&#xff08;Language Model&#xff0c;LM&#xff09;目标是建模自然语言的概率分布&#xff0c;具体目标是构建词序列w1,w2,...,wm的概率分布&#xff0c;即计算给定的词序列作为一个句子出现可能的大小P(w1w2...wm)。但联合概率P的参数量…

0-虚拟机补充知识

虚拟机克隆 如果想要构建服务器集群&#xff0c;没有必要一台一台的去进行安装&#xff0c;只要通过克隆就可以。 快速获得多台服务器主要有两种方式&#xff0c;分别为&#xff1a;直接拷贝操作和vmware的克隆操作 直接拷贝 将之前安装虚拟机的所有文件进行拷贝&#xff0…

git撤销上一次的commit

一行命令 git reset --soft HEAD^如果在vscode上面&#xff0c;就可以

Oracle 截取指定字符到目标串的末尾

SQL&#xff1a; SELECT-- 目标字符串 目标字符串 指定符号 最后一个 最后一个字符位置1 substr( HG/2106010103/YG\FJSJ\SXKTFJ\FJ03_JPHD, instr( HG/2106010103/YG\FJSJ\SXKTFJ\FJ03_…

transformer 笔记

目录 目前在NLP领域当中&#xff0c;主要存在三种特征处理器——CNN、RNN 以及 Transformer&#xff0c;当前Transformer的流行程度已经大过CNN和RNN&#xff0c;它抛弃了传统CNN和RNN神经网络&#xff0c;整个网络结构完全由Attention机制以及前馈神经网络组成。 Transformer…

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

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

Dart - 语法糖(持续更新)

文章目录 前言开发环境中间表示语法糖1. 操作符/运算符&#xff08;?./??/??/../?../.../...?&#xff09;2. 循环&#xff08;for-in&#xff09;3. 函数/方法&#xff08;>&#xff09;4. 关键字&#xff08;await for&#xff09; 最后 前言 通过将dill文件序列化…

【腾讯云 Cloud Studio 实战训练营】沉浸式体验编写一个博客系统

文章目录 前言项目中技术栈新建工作空间登录(注册)Cloud Studio 账号&#xff1a;进入 Cloud Studio 控制台&#xff1a;配置工作空间参数&#xff1a;确认并创建工作空间&#xff1a;项目搭建 配置nuxt 脚手架运行项目报错信息解决错误脚手架运行预览问题 开启博客代码配置lay…

法大大携手盘子女人坊,以数字化唤醒国风摄影新体验

第三方数据显示&#xff0c;目前&#xff0c;我国共有163万家摄影相关企业&#xff0c;有约1900个从事摄影相关业务的品牌&#xff0c;且预计到2025年艺术摄影市场规模将达到7063.18亿元。艺术摄影行业作为在时代进步、科技发展以及人民生活水平提高的推动下逐渐发展起来的行业…

GPT一键化身「AI助理」——自定义指令功能

最近GPT又更新了一个超实用的功能——自定义指令&#xff0c;启用后&#xff0c;你可以给GPT设置一些固定指令&#xff0c;让它记住或扮演某个角色&#xff0c;比如客服、律师、投资管理师、老师、营养师...... 这样&#xff0c;我们就不再需要每次都要打开新的聊天&#xff0c…

spring-websocket在SpringBoot(包含SpringSecurity)项目中的导入

✅作者简介&#xff1a;大家好&#xff0c;我是 Meteors., 向往着更加简洁高效的代码写法与编程方式&#xff0c;持续分享Java技术内容。 &#x1f34e;个人主页&#xff1a;Meteors.的博客 &#x1f96d;本文内容&#xff1a;spring-websocket在SpringBoot(包含SpringSecurity…

瓴羊Quick BI:可视化大屏界面设计满足企业个性需求

大数据技术成为现阶段企业缩短与竞争对手之间差距的重要抓手&#xff0c;依托以瓴羊Quick BI为代表的工具开展内部数据处理分析工作&#xff0c;也成为诸多企业持续获取竞争优势的必由之路。早年间国内企业倾向于使用进口BI工具&#xff0c;但随着瓴羊Quick BI等一众国内数据处…

milvus: 专为向量查询与检索设计的向量数据库

1. 什么是milvus&#xff1f; milvus docs milvus release Milvus的目标是&#xff1a;store, index, and manage massive embedding vectors generated by deep neural networks and other machine learning (ML) models. Milvus 向量数据库专为向量查询与检索设计&#xf…

无涯教程-jQuery - trigger( event, data )方法函数

trigger(event&#xff0c;[data])方法在每个匹配的元素上触发一个事件。 触发事件不仅限于基于浏览器的事件&#xff0c;还可以触发向bind注册的自定义事件。 trigger( event, [data] ) - 语法 selector.trigger( event, [data] ) 这是此方法使用的所有参数的描述- event…

Numpy

系列文章目录 第一章 python数据挖掘基础环境安装和使用 第二章 Matplotlib 文章目录 系列文章目录一、介绍ndarray优势属性使用 二、ndarray的形状三、ndarray的类型四、创建数组的时候指定类型五、基本操作生成数组的方法生成0和1的数组从现有数组生成生成固定范围的数组生成…

【算法与数据结构】222、LeetCode完全二叉树的节点个数

文章目录 一、题目二、一般遍历解法三、利用完全二叉树性质四、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、一般遍历解法 思路分析&#xff1a;利用层序遍历&#xff0c;然后用num记录节点数量。其他的例如…