A和B取數,動態規劃

有一排正數,玩家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中的一個,…

思路:

動態規劃,設定兩個狀態,

分別爲f(i,j)和 s(i,j)分別表示在 序列i..j 之間,先手能得到的最大數目以及後手計算後剩下的最大數目。

所以先手f(i,j) = max(arr[i]+s(i+1,j),arr[j]+s(i,j-1)) 表示目的爲求最大的情況

後手剩下的爲  s(i,j)= min(f(i+1,j),f(i,j-1);  而後手玩家爲了拿到更多,所以他會選擇,留給對方最少的那種情況。

通過這種相互依賴,我們可以寫出遞歸函數,並設定遞歸邊界,

在先手情況下f(i,j),如果 i==j 表示只剩下最後一個,那麼 就直接返回這個數。

 在後手情況下s(i,j),如果 i==j 表示只剩下最後一個,那麼 就直接返回0。因爲後手玩家已經取完了,所以留給先手玩家的就爲0;

接着,通過畫出依賴關係,我們可以發現f和s的相互關係,並且很多情況的遞歸都是重複計算的,所以這裏就出現了無後效性的情況,即後面的計算都不會改變之前已經計算好的。就可以根據這個寫出動態規劃。

代碼:

#include<iostream>
#include<stdio.h>
using namespace std;
int arr[1000] = {0}; 

int N;
//設置兩個狀態圖 
int dps[1000][1000]={0};
int dpf[1000][1000]={0};

int main() {
	
	scanf("%d",&N);
	for(int i=0;i<N;i++){
		scanf("%d",&arr[i]);
	}
	
	
	for (int j=0;j<N;j++){
		//先手狀態初始化 
		dpf[j][j] = arr[j];
		
		for (int i = j-1;i>=0;i--){
			//先手狀態和後手狀態依次計算 
			dpf[i][j] = max(arr[i]+ dps[i+1][j],arr[j]+dps[i][j-1]);
			dps[i][j] = min(dpf[i+1][j],dpf[i][j-1]);
		}
	}
	
	printf("%d\n",dpf[0][N-1]);

	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章