纸牌博弈问题
2017-08-24 10:03
323 查看
问题:
有一排正数,代表数值不同的纸牌排成一条线,玩家A和玩家B都可以看到。
每位玩家在拿走数字的时候,都只能从最左和最右的数中选择一个。
玩家A先拿,玩家B再拿,两人交替拿走所有的数字,
两人都力争自己拿到的数的总和比对方多。请返回最后获胜者的分数。
例如:
5,2,3,4
玩家A先拿,当前他只能拿走5或者4。
如果玩家A拿走5,那么剩下2,3,4。轮到玩家B,此时玩家B可以选择2或4中的一个,…
如果玩家A拿走4,那么剩下5,2,3。轮到玩家B,此时玩家B可以选择5或3中的一个,…
题解:双方都是绝对理性的个体,都会选择对自己最有利的方式。
从最基本的case来分析,如果只剩一张牌,那么先手的拿走这张牌,得到相应分数,后手的得到0;如果剩下两张牌,先手的一定拿走大的那一张,而后手的没得选择;依次类推…..
那么,我们就想,如何表示还剩多少张牌呢?数组下标标识头和尾就好了。
那如何表示先手后手呢?先手和后手只是下标标识不同而已,可用两个函数表示。因为后手是受先手影响的,所以也可以只用一个函数表示。
先从两个函数的来分析,第一个函数f(i,j),表示先手的选手在给定下标标识的情况下可以取得的分数。第二个函数s(i,j),表示后手的选手在给定下标标识的情况下可以取得的分数。
最后要求返回最大值,是整个数组在整个游戏过程结束时A,B两个选手所得的最大值,所以i和j应该标识数组的头和尾。即max(f(0,arr.length-1),s(0,arr.length-1))
然后根据这个逻辑就可以写出暴力递归的伪代码:
public static int win1(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
return Math.max(f(arr, 0, arr.length - 1), s(arr, 0, arr.length - 1));
}
public static int f(int[] arr, int i, int j) {
if (i == j) {
return arr[i];
}
return Math.max(arr[i] + s(arr, i + 1, j), arr[j] + s(arr, i, j - 1));
/*当i!=j时,说明至少还有两张纸牌,那么先手的人就有选择,是先拿第一张,还是拿最后一张。
如果拿了第一张,那么剩下i+1到j的牌里他就是后手,
同理先拿了最后一张,那么他在i到j-1里后手。
都是绝对理性的个体,所以两者选大的拿走即为他的分数。*/
}
public static int s(int[] arr, int i, int j) {
if (i == j) {
return 0;
}
return Math.min(f(arr, i + 1, j), f(arr, i, j - 1));
/*当i!=j时,说明至少还有两张纸牌,那么先手的人就有选择,是先拿第一张,还是拿最后一张。
如果拿了第一张,那么后手的人在剩下i+1到j的牌里他就是先手,
同理如果先手拿了最后一张,那么后手的人在i到j-1里先手。
都是绝对理性的个体,所以先手一定会把更小的留给他。*/
}
接下来就是优化的过程,直接根据递归改动态规划,不是直接就写动归。
分析递归:上述两个函数,分别表示先手的人得到的分数和后手的人得到的分数,可以容易的发现f只与i和j有关,s也只与i和j有关。故:
由此得出根据递归推出的动态规划伪代码:
至此,问题解决,以上。
无后效性问题都可以通过暴力递归直接推出优化的动态规划版本。
有一排正数,代表数值不同的纸牌排成一条线,玩家A和玩家B都可以看到。
每位玩家在拿走数字的时候,都只能从最左和最右的数中选择一个。
玩家A先拿,玩家B再拿,两人交替拿走所有的数字,
两人都力争自己拿到的数的总和比对方多。请返回最后获胜者的分数。
例如:
5,2,3,4
玩家A先拿,当前他只能拿走5或者4。
如果玩家A拿走5,那么剩下2,3,4。轮到玩家B,此时玩家B可以选择2或4中的一个,…
如果玩家A拿走4,那么剩下5,2,3。轮到玩家B,此时玩家B可以选择5或3中的一个,…
题解:双方都是绝对理性的个体,都会选择对自己最有利的方式。
从最基本的case来分析,如果只剩一张牌,那么先手的拿走这张牌,得到相应分数,后手的得到0;如果剩下两张牌,先手的一定拿走大的那一张,而后手的没得选择;依次类推…..
那么,我们就想,如何表示还剩多少张牌呢?数组下标标识头和尾就好了。
那如何表示先手后手呢?先手和后手只是下标标识不同而已,可用两个函数表示。因为后手是受先手影响的,所以也可以只用一个函数表示。
先从两个函数的来分析,第一个函数f(i,j),表示先手的选手在给定下标标识的情况下可以取得的分数。第二个函数s(i,j),表示后手的选手在给定下标标识的情况下可以取得的分数。
最后要求返回最大值,是整个数组在整个游戏过程结束时A,B两个选手所得的最大值,所以i和j应该标识数组的头和尾。即max(f(0,arr.length-1),s(0,arr.length-1))
然后根据这个逻辑就可以写出暴力递归的伪代码:
public static int win1(int[] arr) {
if (arr == null || arr.length == 0) {
return 0;
}
return Math.max(f(arr, 0, arr.length - 1), s(arr, 0, arr.length - 1));
}
public static int f(int[] arr, int i, int j) {
if (i == j) {
return arr[i];
}
return Math.max(arr[i] + s(arr, i + 1, j), arr[j] + s(arr, i, j - 1));
/*当i!=j时,说明至少还有两张纸牌,那么先手的人就有选择,是先拿第一张,还是拿最后一张。
如果拿了第一张,那么剩下i+1到j的牌里他就是后手,
同理先拿了最后一张,那么他在i到j-1里后手。
都是绝对理性的个体,所以两者选大的拿走即为他的分数。*/
}
public static int s(int[] arr, int i, int j) {
if (i == j) {
return 0;
}
return Math.min(f(arr, i + 1, j), f(arr, i, j - 1));
/*当i!=j时,说明至少还有两张纸牌,那么先手的人就有选择,是先拿第一张,还是拿最后一张。
如果拿了第一张,那么后手的人在剩下i+1到j的牌里他就是先手,
同理如果先手拿了最后一张,那么后手的人在i到j-1里先手。
都是绝对理性的个体,所以先手一定会把更小的留给他。*/
}
接下来就是优化的过程,直接根据递归改动态规划,不是直接就写动归。
分析递归:上述两个函数,分别表示先手的人得到的分数和后手的人得到的分数,可以容易的发现f只与i和j有关,s也只与i和j有关。故:
由此得出根据递归推出的动态规划伪代码:
public static int win2(int[] arr) { if (arr == null || arr.length == 0) { return 0; } int[][] f = new int[arr.length][arr.length]; int[][] s = new int[arr.length][arr.length]; for (int j = 0; j < arr.length; j++) { f[j][j] = arr[j]; for (int i = j - 1; i >= 0; i--) { f[i][j] = Math.max(arr[i] + s[i + 1][j], arr[j] + s[i][j - 1]); s[i][j] = Math.min(f[i + 1][j], f[i][j - 1]); } } return Math.max(f[0][arr.length - 1], s[0][arr.length - 1]); }
至此,问题解决,以上。
无后效性问题都可以通过暴力递归直接推出优化的动态规划版本。
相关文章推荐
- 纸牌博弈问题
- 纸牌博弈问题
- 纸牌博弈问题
- 纸牌博弈问题
- 动态规划-排成一条线的纸牌博弈问题
- 递归与动态规划---排成一条线的纸牌博弈问题
- 动态规划-排成一条线的纸牌博弈等问题
- 分金子(360公司2017春招真题) 纸牌博弈问题 程序员代码面试指南
- 排成一条线的纸牌博弈问题
- 排成一条线的纸牌博弈问题
- 纸牌博弈问题
- 排成一条线的纸牌博弈问题
- 纸牌博弈问题 动态规划
- 博弈问题,sg函数初步学习例题解析
- 博弈问题
- 博弈树,动态规划(计算好的子问题存储起来,以后直接取用)
- 发纸牌问题---多维数组
- hdoj problem 1525 Euclid's Game (博弈问题(寻找关键点))
- 寻找必败态——一类博弈问题的快速解法
- 取石子问题--巴什博弈