先手和后手零和博弈拿值,请问谁最后得分高,得多少分

2023-12-14 23:10

本文主要是介绍先手和后手零和博弈拿值,请问谁最后得分高,得多少分,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

先手和后手零和博弈拿值,请问谁最后得分高?得多少分?

提示:DP2:从L--R范围上的尝试模型


文章目录

  • 先手和后手零和博弈拿值,请问谁最后得分高?得多少分?
    • @[TOC](文章目录)
  • 题目
  • 一、审题
  • 二、解题
  • 根据暴力递归改动态规划填表的代码
  • 还有一种暴力递归改动态规划的填表方法,斜着来
  • 总结

题目

先手,和后手,
先手先在数组arr上拿值,可以从左边拿,也可以从右边拿
但是先手后手是零和博弈,也就是,任何人,我目前拿一个值,有利于我自己得分高,同时也要想办法让对手下次拿的得分低。
请问,先手后手谁拿的分高?最高是多少?


一、审题

示例1:
arr= 3 100 7 10
(1)先手先拿,不管如何,先手要拿一个对自己有利的,但是也让对收下次拿尽量少,所以先手不能拿3,因为对手下次能拿100,于是先手选择拿10
arr=3 100 7
(2)后手拿,后手也要拿一个更大的值,但是希望对手不要拿到更好的,没什么办法,后手只能拿7
arr=3 100
(3)先手拿,先手要拿更大的,让对手拿一个最小的,拿走100
arr=3
(4)后手拿,就一个值,自己拿了3
所以先手赢了,综合10+100=110

示例2:
arr= 3 100 7
(1)先手先拿,不管如何,先手要拿一个对自己有利的,但是也让对收下次拿尽量少,所以先手不能拿3,选择拿10
arr= 3 100
(2)后手拿,拿更大的,让对手拿小的,拿走100
arr=3
(3)先手拿,就一个值,自己拿了3
所以后手赢了,综合100


二、解题

涉及到零和博弈:
从arr的0–N-1范围上,先手拿
从arr的0–N-1范围上,后手拿
轮番拿,看谁的得分更大?

事情是这样的,先手拿完一个值,可以选arr中的L,或者R,我希望拿最大值,然后先手立马就变后手了,
变后手了以后,只能拿最小的【因为零和博弈,对手绝对不会给我留大的数!】

所以,我们定义一个函数,f(arr,L,R) 是先手从L–R范围内拿,拿到的最大得分。
(1)那么我们f(arr,L,R)内部咋处理呢?
——如果L=R,则就一个数,先手直接拿了,返回arr[L]
刚刚说了,先手自由选L或者R,然后作为后手去拿剩下的部分;
——不妨设我先手先拿了L,此后就只能以后手的身份去L+1–R上拿了。得分是p1 = arr[L]+second(arr,L+1,R)
在这里插入图片描述
——不妨设,先手先拿了R,则此后,先手只能以后手的身份去拿L–R-1了,得分是p2 = arr[R]+second(arr,L,R-1)
在这里插入图片描述
究竟先手拿左边更好呢?还是拿右边更好呢?
很简单,选max(p1,p2)返回即可;

对于后手呢?后手从L–R上拿,自己的最大得分是多少?
(2)定义为:second(arr,L,R)
——如果L=R,则就一个数,先手肯定直接拿了,既然我此刻是后手,那很不好意思,我没有啥可以拿,得分为0;惨
——如果先手拿了L,那我现在的身份就是先手了,但是我的对手(先手),刚刚绝不会给我留下好结果的,所以我只能拿差的结果,
但不管如何,我此刻以先手的身份去拿,尽量还是拿一个最好的值:s1 = f(arr,L+1,R)
在这里插入图片描述
——如果先手拿走了R,那后手此刻以先手的身份去拿L–R-1上的,尽量拿对我后手更好的结果:s2=f(arr,L,R-1)
在这里插入图片描述
只不过,不管人先手曾经拿了L还是拿了R,我后手,现在以先手的身份拿,绝对只能拿到刚刚先手留给我的最差结果,因为规则就是零和博弈!!!
于是我后手拿最好的结果,其实也是先手留给我的最差结果,返回:
min(s1,s2);

看见了没?
先手返回就是max(p1,p2),而后手就是min(s1,s2)
为啥呢?先手先拿,当然拿走最好的,但是这个先手留给后手的结果只能是最差的,这就是零和博弈的本质!所以
即使后手拿的时候,以先手的身份拿,但是后手就是拿最小值,因为先手不会给后手留好的结果
——一定要把这个逻辑捋清楚,先手先拿,必定拿最好,留给后手的结果必定最差。——零和博弈

好,知道了这个情况,咱们就可以撸代码了
(1)先手先拿得分的代码:

//复习://我们定义一个函数,**f(arr,L,R)**是先手从L--R范围内拿,拿到的最大得分。public static int f(int[] arr, int L, int R){//(1)那么我们f(arr,L,R)内部咋处理呢?//——如果L=R,则就一个数,先手直接拿了,返回arr[L]if (L == R) return arr[L];//刚刚说了,先手自由选L或者R,然后作为后手去拿剩下的部分;//——不妨设我先手先拿了L,此后就只能以后手的身份去L+1--R上拿了。得分是p1 = arr[L]+**second(arr,L+1,R)**int p1 = arr[L] + g(arr, L + 1, R);//——不妨设,先手先拿了R,则此后,先手只能以后手的身份去拿L--R-1了,得分是p2 = arr[R]+**second(arr,L,R-1)**int p2 = arr[R] + g(arr, L, R - 1);//究竟先手拿左边更好呢?还是拿右边更好呢?//很简单,选**max**(p1,p2)返回即可;return Math.max(p1, p2);}

(2)后手后拿得分的代码:

//(2)定义为:**second(arr,L,R)**public static int g(int[] arr, int L, int R) {//——如果L=R,则就一个数,先手肯定直接拿了,既然我此刻是后手,那很不好意思,我没有啥可以拿,得分为0;惨if (L == R) return 0;//——如果先手拿了L,那我现在的身份就是先手了,但是我的对手(先手),刚刚绝不会给我留下好结果的,所以我只能拿差的结果,//但不管如何,我此刻以先手的身份去拿,尽量还是拿一个最好的值:**s1 = f(arr,L+1,R)**int s1 = f(arr,L + 1, R);//——如果先手拿走了R,那后手此刻以先手的身份去拿L--R-1上的,尽量拿对我后手更好的结果:**s2=f(arr,L,R-1)**int s2 = f(arr, L, R - 1);//只不过,不管人先手曾经拿了L还是拿了R,我后手,现在以先手的身份拿,**绝对只能拿到刚刚先手留给我的最差结果**,因为规则就是零和博弈!!!//于是我后手拿最好的结果,其实也是先手留给我的最差结果,返回:**min**(s1,s2);return Math.min(s2, s1);}

OK,任意数组,就像案例1和案例2,可能先手还会输呢?究竟鹿死谁手?
咱们这样做,直接对比先手拿,和后手拿,看看谁更大就得了呗。

//主函数调用public static int maxScore(int[] arr){if (arr == null || arr.length == 0) return 0;int N = arr.length;//左边先手拿,后边后手拿,反正都是arr整体范围上去拿就行,看看谁赢?return Math.max(f(arr, 0, N - 1), g(arr, 0,  N - 1));}

用案例测试一把:

数组13 100 7 10 
110数组23 100 7 
100

是不是很完美?

根据暴力递归改动态规划填表的代码

上面的暴力递归,2个函数f和g,每个函数都是有L和R俩变量,自然需要填2个表,都是2维的信息表

针对这个题,先手和后手都是L–R范围上去拿,这种动态规划的类型是从L–R范围上的尝试模型
有固定的的填表套路的,正常情况下L<=R,L>R是不可能的

比如,针对先手得分的暴力递归函数:f(int[] arr, int L, int R)
L和R都可以取0–N-1范围
则就是一个二维表dpf,每个格子dpf[L][R]:代表每一种L和R取值下,先手从L–R范围上拿值,得到的最大得分是多少?
(0)看图中五角星那个格子,dpf[0][N-1]=f(arr,0, N-1)就代表我们要的结果,先手从0–N-1范围上拿值,最大得分是?
(1)当L>R不可能,所以表格的左下角部分,都不用填哦!!!
(2)当L=R就是表中的主对角线,自然dpf[L][R] = arr[L],这是根据暴力递归中的代码来改的
(3)当R=L+1就是表中的副对角线,自然看L和R谁大,取谁,dpf[L][R] = max(arr[L],arr[R]),你想想是不是,就俩值,先手肯定拿走最大的呗。
在这里插入图片描述
(4)任意位置dpf[L][R] 怎么填?
咱们从最后N-2那行开始,每次从表格中蓝色圈1开始填,然后一行一行往上推,
行起点是L=N-3行一直到0行,列起点R=N-1列即R就是L+2,一直到N-1列;这样按顺序填表
咋填一会说

先看后手的表格怎么填写?
针对后手得分的暴力递归函数:g(int[] arr, int L, int R)
L和R都可以取0–N-1范围
则就是一个二维表dps,每个格子dps[L][R]:代表每一种L和R取值下,后手从L–R范围上拿值,得到的最大得分是多少?
由于是后手,肯定不会得到啥好结果的
(2-0)看图中五角星那个格子,dps[0][N-1]=g(arr,0, N-1)就代表我们要的结果,后手从0–N-1范围上拿值,最大得分是?
(2-1)当L>R不可能,所以表格的左下角部分,都不用填哦!!!
(2-2)当L=R就是表中的主对角线,自然dps[L][R] = 0,这是根据暴力递归中的代码来改的,先手拿走了,后手没得拿,惨!
(2-3)当R=L+1就是表中的副对角线,自然看L和R谁,取谁,dps[L][R] = min(arr[L],arr[R]),你想想是不是,就俩值,先手肯定拿走最大的呗,后手只能拿最小的!
在这里插入图片描述
(2-4)任意位置dps[L][R] 怎么填?
咱们从最后N-2那行开始,每次从表格中蓝色圈1开始填,然后一行一行往上推,
行起点是L=N-3行一直到0行,列起点R=N-1列即R就是L+2,一直到N-1列;这样按顺序填表

发现了吗?这个填表顺序竟然和先手一样的顺序
而且,先手暴力递归代码中,有这些:

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

转化为dpf格子就是这样的

int p1 = arr[L] + dps[L + 1][R];
int p2 = arr[R] + dps[L][R - 1];

先手的格子,依赖后手的格子dps!!!
同样,后手的格子依赖先手的格子dpf!!!
后手暴力递归的代码中有这俩:

int s1 = f(arr,L + 1, R);
int s2 = f(arr, L, R - 1);

转化为填表的格子是这样的

int s1 = dpf[L + 1][R];
int s2 = dpf[L][R - 1];

相互依赖,那咱们用在同一个宏观调度下,填上面蓝色圈1–圈6中

//然后倒回来填表--固定模式
咱们从最后N-2那行开始,每次从表格中蓝色圈1开始填,然后一行一行往上推,
行起点是L=N-3行一直到0行,列起点R=N-1列即R就是L+2,一直到N-1列;这样按顺序填表for (int i = N - 2; i >= 0; i--) {for (int j = i + 2; j < N; j++) {在这里面去填写剩下的格子,按照顺序

整体我们决定谁回赢呢??
那就要看两个表中五角星那个格子,谁更大?
下面的i=L, j=R,不必疑惑!

//复习:根据暴力递归改动态规划填表的代码public static int getMaxValue3DP(int[] arr){if (arr == null || arr.length == 0) return 0;int N = arr.length;int[][] dpf = new int[N][N];//造表--默认全0int[][] dps = new int[N][N];//造表--默认全0//根据终止条件,填写对角线for (int i = 0; i < N; i++) {dpf[i][i] = arr[i];//s表,全0}//填写副对角for (int i = 0; i <= N - 2; i++) {//i控制行dpf[i][i + 1] = Math.max(arr[i], arr[i + 1]);dps[i][i + 1] = Math.min(arr[i], arr[i + 1]);}//然后倒回来填表--固定模式for (int i = N - 2; i >= 0; i--) {for (int j = i + 2; j < N; j++) {//咱们从最后N-2那行开始,每次从表格中蓝色圈1开始填,然后一行一行往上推,//行起点是L=N-3行一直到0行,列起点R=N-1列即R就是L+2,一直到N-1列;这样按顺序填表//根据暴力递归来--先手先拿//拿左边int p1 = arr[i] + dps[i + 1][j];//拿右边int p2 = arr[j] + dps[i][j - 1];//是更大,先手要谁?dpf[i][j] = Math.max(p1, p2);//我一定拿最好的//然后才能轮到后手拿——自然拿最差的结果,零和博弈//如果先手拿走了Lint p3 = dpf[i + 1][j];//如果先手拿走了Rint p4 = dpf[i][j - 1];//咋赵我都只能拿最次的dps[i][j] = Math.min(p3, p4);}}//先手后手都拿自己的五角星那个格子,对比谁大,谁赢了。return Math.max(dpf[0][N - 1], dps[0][N - 1]);//最终返回的最值}public static void test2(){int[] arr = {3, 100, 7, 10};int[] arr2 = {3, 100, 7};System.out.println("\n数组1:");for(Integer i : arr) System.out.print(i +" ");System.out.println();System.out.println(getMaxValue3DP(arr));System.out.println("\n数组2:");for(Integer i : arr2) System.out.print(i +" ");System.out.println();System.out.println(getMaxValue3DP(arr2));}public static void main(String[] args) {
//        test();test2();}

结果:

数组13 100 7 10 
110数组23 100 7 
100

你就说牛不牛吧!!!
这个题目,先手后手谁赢的动态规划代码,无非就是填写两个表格,而且俩表填写的时候,相互依赖的!
根据宏观调度,我们最后一个填写五角星那个格子,比较谁大,谁就行了。


还有一种暴力递归改动态规划的填表方法,斜着来

上面一节,咱们讲的是非常标准的一种动态规划填表法,你见了一定要学会

下面这种可以了解,但是很有趣,咱们啊,不用从蓝色圈1–6填
咱们愣是给他斜着填也行呀
只不过i和j要同时++

在这里插入图片描述
(1)最开始,咱们只填写主对角线,即可

//根据终止条件,填写对角线for (int i = 0; i < N; i++) {f[i][i] = arr[i];//s表,全0}

(2)然后从粉色,橘色,蓝色,红色依次斜着填表!
行从1行到N-1行,列的话,从j=1–N-1列不断递增
每次填格子都是L++,R++,这样保证斜着走!

//然后从副对角开始推理for (int j = 1; j < N; j++) {int L = 0;//行int R = j;//这样代表挨个操作副对角线--一会L++,R++//这种操作对角线的模式,学一下while (L < N && R < N) {

整体填表就是这样的代码:

//既然是俩递归函数,显然就是要填写两个表f,和s//而且,两个表的递归是相互推理的,那就是表之间相互推理,//怎么推,还得画个图,看笔记//根据终止条件,我们能得到第一批值,那就是对角线,一个是arr[L],一个是全0//表是一个矩阵,L*R的矩阵,L从0--N,R,从0--N//最后要求的其实是max(f[0][N-1],s[0][N-1])//然后f表往往还得看s表,由递归函数可知,假设我点在L,R处// 往往f表由s的L+1,或者R-1来推理,也就是我一个点的下边一个点,// 或者我一点的左边一个点,也就是我们填表,只需要考虑对角线右边那些//直到我们填写结束,拿到f[0][N-1],和s[0][N-1],求取最大值public static int getMaxValue2(int[] arr){if (arr == null || arr.length == 0) return 0;int N = arr.length;int[][] f = new int[N][N];//造表--默认全0int[][] s = new int[N][N];//造表--默认全0//根据终止条件,填写对角线for (int i = 0; i < N; i++) {f[i][i] = arr[i];//s表,全0}//然后从副对角开始推理for (int j = 1; j < N; j++) {int L = 0;//行int R = j;//这样代表挨个操作副对角线--一会L++,R++//这种操作对角线的模式,学一下while (L < N && R < N) {//对于所有副对角线元素//根据暴力递归来--先手先拿//拿左边int p1 = arr[L] + s[L + 1][R];//拿右边int p2 = arr[R] + s[L][R - 1];f[L][R] = Math.max(p1, p2);//我一定拿最好的//然后后手拿//如果先手拿走了Lint p3 = f[L + 1][R];//如果先手拿走了Rint p4 = f[L][R - 1];//咋招我都只能拿最次的s[L][R] = Math.min(p3, p4);L++;R++;//都递增就是去副对角线一侧,斜着走}}return Math.max(f[0][N - 1], s[0][N - 1]);//最终返回的最值}

测试一下:

    public static void test2(){int[] arr = {3, 100, 7, 10};int[] arr2 = {3, 100, 7};System.out.println("\n数组1:");for(Integer i : arr) System.out.print(i +" ");System.out.println();System.out.println(getMaxValue3DP(arr));System.out.println(getMaxValue2(arr));System.out.println("\n数组2:");for(Integer i : arr2) System.out.print(i +" ");System.out.println();System.out.println(getMaxValue3DP(arr2));System.out.println(getMaxValue2(arr2));}public static void main(String[] args) {
//        test();test2();}

结果:

数组13 100 7 10 
110
110数组23 100 7 
100
100

看见没?
斜着填表也行,只不过没有横着填来得方便和直接,但是也是最优解!!!
好好了解一下哦


总结

提示:重要经验:

1)暴力递归DP2:从L–R范围上的尝试模型,考虑L或R位置的情况,然后返回各自情况下的最好答案
2)从暴力递归修改为动态规划的代码,就是填表,这里是范围上的填表方式,有固定的的套路,先主对角,副对角,然后倒回来填,有时候,两个表相互依赖,怎么依赖,完全看暴力递归的代码来改写!
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

这篇关于先手和后手零和博弈拿值,请问谁最后得分高,得多少分的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/494241

相关文章

poj2505(典型博弈)

题意:n = 1,输入一个k,每一次n可以乘以[2,9]中的任何一个数字,两个玩家轮流操作,谁先使得n >= k就胜出 这道题目感觉还不错,自己做了好久都没做出来,然后看了解题才理解的。 解题思路:能进入必败态的状态时必胜态,只能到达胜态的状态为必败态,当n >= K是必败态,[ceil(k/9.0),k-1]是必胜态, [ceil(ceil(k/9.0)/2.0),ceil(k/9.

hdu3389(阶梯博弈变形)

题意:有n个盒子,编号1----n,每个盒子内有一些小球(可以为空),选择一个盒子A,将A中的若干个球移到B中,满足条件B  < A;(A+B)%2=1;(A+B)%3=0 这是阶梯博弈的变形。 先介绍下阶梯博弈: 在一个阶梯有若干层,每层上放着一些小球,两名选手轮流选择一层上的若干(不能为0)小球从上往下移动,最后一次移动的胜出(最终状态小球都在地面上) 如上图所示,小球数目依次为

AI模型的未来之路:全能与专精的博弈与共生

人工智能(AI)领域正迅速发展,伴随着技术的不断进步,AI模型的应用范围也在不断扩展。当前,AI模型的设计和使用面临两个主要趋势:全能型模型和专精型模型。这两者之间的博弈与共生将塑造未来的AI技术格局。本文将从以下七个方面探讨AI模型的未来之路,并提供实用的代码示例,以助于研究人员和从业者更好地理解和应用这些技术。 一、AI模型的全面评估与比较 1.1 全能型模型 全能型AI模型旨在在多

简单取石子游戏~博弈

很坑爹的小游戏,至于怎么坑爹,嘎嘎~自己研究去吧~! #include<stdio.h>#include<windows.h>#include<iostream>#include<string.h>#include<time.h>using namespace std;void Loc(int x,int y);/*定位光标*/void Welcome(); /*创建欢迎界面*/

综合评价 | 基于熵权-变异系数-博弈组合法的综合评价模型(Matlab)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 根据信息熵的定义,对于某项指标,可以用熵值来判断某个指标的离散程度,其信息熵值越小,指标的离散程度越大, 该指标对综合评价的影响(即权重)就越大,如果某项指标的值全部相等,则该指标在综合评价中不起作用。因此,可利用信息熵这个工具,计算出各个指标的权重,为多指标综合评价提供依据。 变异系数只在平均值不为

“苹果税”引发的苹果与腾讯、字节跳动之间的纷争与博弈

北京时间9月10日凌晨一点的Apple特别活动日渐临近,苹果这次将会带来iPhone16系列新品手机及其他硬件产品的更新,包括iPad、Apple Watch、AirPods等。从特别活动的宣传图和宣传标语“閃亮時刻”来看,Apple Intelligence将会是史上首次推出,无疑将会是iOS 18的重头戏和高光时刻。 不过就在9月2日,一则“微信可能不支持iPhone16”的

美业收银系统怎么选择?博弈美业系统展示、美业SaaS管理系统源码戳

美业收银系统是一种专为美容、美发、美甲、SPA等美业门店设计的全面性结账解决方案,其重要性在于它为门店提供了全面的业务管理功能。美业收系统可以处理销售、预约管理、库存追踪和员工绩效等多项任务,不仅能够简化交易流程,还能提高门店管理效率,是提升门店竞争力和盈利能力的利器。 一套优秀的美业收银系统要专业、智能、高效、便捷!博弈美业包括PC、pad、手机APP、小程序四大端口,一套系统解决连锁美业多种

智能对决:提示词攻防中的AI安全博弈

智能对决:提示词攻防中的AI安全博弈 在2024年上海AIGC开发者大会上,知名提示词爱好者工程师云中嘉树发表了关于AI提示词攻防与安全博弈的精彩演讲。他深入探讨了当前AI产品的安全现状,提示词攻击的常见手段及其应对策略。本文将对他的演讲进行详细的解读与分析,并结合实际案例和技术手段,探讨如何在AI应用开发中提高安全性。 1. AI产品安全现状 随着大模型(如GPT系列)和AI应用的普及,A

综合评价 | 基于层次-熵权-博弈组合法的综合评价模型(Matlab)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 AHP层次分析法是一种解决多目标复杂问题的定性和定量相结合进行计算决策权重的研究方法。该方法将定量分析与定性分析结合起来,用决策者的经验判断各衡量目标之间能否实现的标准之间的相对重要程度,并合理地给出每个决策方案的每个标准的权数,利用权数求出各方案的优劣次序,比较有效地应用于那些难以用定量方法解决的课题

拍卖与博弈:计算广告中的底价问题

流量变现和RTB 现代计算广告中,最广泛的流量交易模式为实时竞价模式,即Real-Time-Bidding(RTB)。实时竞价顾名思义,就是在流量到达时被放到交易市场进行公开的,实时的竞拍,参与竞拍的广告主赢得竞拍后,即可获得对这个流量的投放权,整个流程如图示。 以新浪微博的信息流广告为例,当我们刷微博时,微博的信息流(或者timeline)中会夹杂着广告,假设微博将信息流的第4和第10位作为