1. 程式人生 > >博弈論之雙人取數遊戲詳解

博弈論之雙人取數遊戲詳解

描述

有如下一個雙人遊戲:N(2 <= N <= 100)個正整數的序列放在一個遊戲平臺上,遊戲由玩家1開始,兩人輪流從序列的兩端取數,取數後該數字被去掉並累加到本玩家的得分中,當數取盡時,遊戲結束。以最終得分多者為勝。

編一個執行最優策略的程式,最優策略就是使玩家在與最好的對手對弈時,能得到的在當前情況下最大的可能的總分的策略。你的程式要始終為第二位玩家執行最優策略。

格式

PROGRAM NAME: game1

INPUT FORMAT:

(file game1.in)

第一行: 正整數N, 表示序列中正整數的個數。

第二行至末尾: 用空格分隔的N個正整數(大小為1-200)。

OUTPUT FORMAT

:

(file game1.out)

只有一行,用空格分隔的兩個整數: 依次為玩家一和玩家二最終的得分。

SAMPLE INPUT

6 
4 7 2 9 5 2

SAMPLE OUTPUT

18 11

思路:用遞迴會看起來清晰一點,定義一個dfs方法,返回如果取區間ns第i到第j個數時的最大值。

這裡我就單純遞迴,可以自己加一個記憶陣列提高效率

public class 雙端取求遊戲 {
	
	static int[][] sum = new int[1000][1000];//儲存ns內的各區間和,sum[0][3]表示第0到第3個數的和 
	static int[] ns = new int[]{4,7,2,9,5,2};//能取的數
	
	/*用處: 得到ns區間中第i到j個數先手能拿到的最大值 */
	static int dfs(int i,int j){
		if (i==j) {//如果只有一個數,返回他本身
			return ns[i]; 
		}
		if (Math.abs(i-j)==1) {//如果是兩個數的局面,先手的取那個大的就行了
			return Math.max(ns[i],ns[j]);
		}
/*		大於兩個數的局面,先手試探取首數和尾數後把子局面扔給下一個人,從兩個子局面中挑一個分數大的
		sum[i+1][j] - dfs(i+1, j)+ns[i] 是取完左邊的數後能獲得的最大值
		sum[i][j-1] - dfs(i, j-1)+ns[j] 是取完右邊的數後能獲得的最大值
		先手把子局面交給後手後,先手得到的分應該是區間總和-子局面中先手獲得的分+取的首數或尾數
		*/
		return Math.max(sum[i+1][j] - dfs(i+1, j)+ns[i], sum[i][j-1] - dfs(i, j-1)+ns[j]);
	}
	
	//初始化sum陣列
	static void initSum(){
		for (int i = 0; i < ns.length; i++) {
			int tmp = 0;
			for (int j = i; j < ns.length; j++) {
				tmp += ns[j];
				sum[i][j] = tmp;
			}
		}
	}
	
	public static void main(String[] args) {
		initSum();
		System.out.println(dfs(0, 5)+" " + (sum[0][5]-dfs(0, 5)));
		
	}

}