從數組兩頭抽取元素遊戲
給定一個數組,玩家A,B每次從數組頭或尾取數,且只能從頭尾取。假定A,B都絕頂聰明,均采取最優策略,判斷A先手的情況下,A是否能夠獲勝。
分析: f(i, j) 表示 在arr[i~j]中A 先手時能夠獲得的最大分數,s(i, j) 表示 A後手時能夠獲得的最大分數。
首先分析f(i, j)。 A可先取arr[i], 取完後剩余arr[i+1, j]。此時相當於A後手在[i+1, j]的情況了。
也可先取arr[j], 取完後剩余arr[i, j - 1]。 此時相當於A後手在[i, j -1]的情況了。
則 f(i, j) = max{arr[i] + s(i+1, j), arr[j] + s(i, j-1)}
再分析s(i, j)。B可先取arr[i] 或 arr[j] 。取完後相當於A先手的情況了。只是在這種情況下,B會留下最差解。
s(i, j) = min{arr[i] + f(i+1, j), arr[j] + f(i, j-1)};
故可生成兩個二維矩陣。代碼如下:
邊界條件:
f[j][j] = arr[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 f[0][arr.length - 1], s[0][arr.length - 1]; }
上述方法時間復雜度O(N2),用到了兩個二維數組。可以在空間復雜度上稍微優化一下,只使用1個二維數組。
用dp[i][j]表示在arr[i~j]中A先手的情況能取得的最大分數。則A的這一次先手與下一次先手有4種可能:
1. A 取 arr[i], B 取 arr[j] , 剩下[i+1, j-1]
2. A 取 arr[i], B 取arr[i+1], 剩下[i+2, j]
3. A 取 arr[j], B 取arr[i], 剩下[i+1, j-1]
4. A 取arr[j] , B 取arr[j-1], 剩下[i, j-2]
前兩種情況取最小情況,後兩種情況取最小情況,然後取最大值。其實就是綜合之前的情況。
邊界條件:
i == j時,就是取arr[i]; 同時,此種情況必須保證j >= i +2, 即至少有3個數。代碼如下:
public static int win1(int[] arr){ if(arr == null || arr.length == 0) return 0; int len = arr.length; int[][] dp = new int[len][len]; int part1, part2, part3, part4; for(int i = 0; i < len; i++) dp[i][i] = arr[i]; for(int i = len - 1; i >= 0; i--) for(int j = i+1; j < len; j++){ if( j >= i+2 ){ part1 = ( i <= len -3 ? (arr[i] + dp[i+2][j]) : 0 ); part2 = ((i <= len -2 && j >= 1) ? (arr[i] +dp[i+1][j]) : 0); part3 = (j >= 2 ? (arr[j] + dp[i][j-2]) : 0); part4 = ((j >= 1 && i <= len-2) ? (arr[j] + dp[i+1][j-1]) : 0); dp[i][j] = Math.max(Math.min(part1, part2), Math.min(part3, part4)); }else dp[i][j] = Math.max(arr[i], arr[j]); } return dp[0][len-1]; }
從數組兩頭抽取元素遊戲