通用算法 - [博弈論] - 雙人取數遊戲

1、問題描述

給定一個數組nums,a、b兩人輪流從數組的左端或右端取一個數作爲自己的得分,假設兩人足夠聰明,都採用最優的策略取數,且a先取,問a能能拿到的最大的分數是多少?
示例:

輸入:nums=[4,7,5,3]
輸出:10
解釋:a能拿到的最大分數爲7+3=10.

2、解題思路

分析:由題意,我們可以明確以下幾點:
(1) 當選手aabb在子數組nums[i,...,j]nums[i,...,j]中取數時,無論怎麼取,aabb最終的得分之和總是一個固定值,這個固定值等於子數組的所有元素之和;
(2) 假設dp[i][j]dp[i][j]表示aa選手在子數組nums[i,...,j]nums[i,...,j]取數時的最優解,由於bb選手每次也是選擇最優的解,所以dp[i1][j]dp[i-1][j]或者dp[i][j1]dp[i][j-1]就代表了bb選手在子數組nums[i,...,j]nums[i,...,j]取數時的最優解(因爲aa選手取了一個數後剩下的元素要麼是nums[i+1,...,j]nums[i+1,...,j]要麼是nums[i,...,j1]nums[i,...,j-1])。
(3) 考慮到aabb的得分之和固定,即aa的得分加上bb的得分等於固定值,若想讓aa選手的得分最高,則等價於讓bb的得分最低。

根據以上結論,我們採用動態規劃來解決該問題:
(1)定義狀態:

dp[i][j]dp[i][j]aa選手在子數組nums[i,...,j]nums[i,...,j]上取數時的最大得分;

(2)狀態轉移:
aa選手在子數組nums[i,...,j]nums[i,...,j]的得分等於子數組之和sum(i,j)sum(i,j)減去bb選手子數組nums[i,...,j]nums[i,...,j]的得分,而aa選手要想得分最高,等價於bb選手的得分最低。

dp[i][j]=sum(i,j)min(dp[i+1][j],dp[i][j1])dp[i][j] = sum(i,j) - min(dp[i+1][j],dp[i][j-1])

(3)確定起始:
當只剩下一個數時,aa取走它。
dp[i][i]=nums[i]dp[i][i]=nums[i]
(4)確定終止
aa選手在整個數組取數時的最大得分。
dp[0][length1]dp[0][length-1]

注意,由於涉及到區間數組和的問題,因此可以採用前綴和來進行優化。

3、代碼實現

int maxScore(vector<int> nums){
	int len = nums.size();
	//求數組的前綴和數組
	vector<int> sums(len,0);
	for(int i = 0; i < len; i++){
		if(i > 0){
			sums[i] = sums[i-1] + nums[i]
		}
		else{
			sums[i] = nums[i];
		}
	}
	
	//動態規劃
	vector<vector<int>> dp(len, vector<int>(len,0));
	for(int i = 0; i < len; i++){
		for(int j=i; j >=0; j--){
			if(j == i){
				dp[i][i] = nums[i];
			}
			else{
				int scoresum = j  > 0 ? sum[i] - sum[j-1] : sum[i];
				dp[j][i] = scoresum - min(dp[j+1][i],dp[j][i-1];
			}
		}
	}
	
	return dp[0][len -1];
			
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章