動態規劃 排成一條線的紙牌博弈等問題

2021-07-22 22:43:04 字數 3784 閱讀 8670

有乙個整型陣列a,代表數值不同的紙牌排成一條線。玩家a和玩家b依次拿走每張紙牌,規定玩家a先拿,玩家b後拿,但是每個玩家每次只能拿走最左或最右的紙牌,玩家a和玩家b都絕頂聰明,他們總會採用最優策略。請返回最後獲勝者的分數。

給定紙牌序列a及序列的大小n,請返回最後分數較高者得分數(相同則返回任意乙個分數)。保證a中的元素均小於等於1000。且a的大小小於等於300。

測試樣例:

[1,2,100,4],4

返回:101

用暴力遞迴的方法:

定義遞迴函式f(i,j),表示如果arr[i...j]這個排列上的紙牌被絕頂聰明的人拿走,最終能夠獲得什麼分數。

定義遞迴函式s(i,j),表示如果a[i..j]這個排列上的紙牌被絕頂聰明的人後拿,最終能獲得什麼分數。

首先來分析,具體過程如下:

1.如果i==j(只有一張紙牌),會被先拿紙牌的人拿走,所以返回arr[i];

2.如果i!=j,先拿紙牌的人有兩種選擇,要麼拿走arr[i],要麼拿走arr[j];

如果拿走arr[i],剩下arr[i+1,j]。對於arr[i+1,j]的紙牌,當前玩家成了後拿的人,因此他後續能獲得的分數為s(i+1,j).如果拿走arr[j],那麼剩下arr[i,j-1],當前玩家後續能獲得的分數為s[i,j-1],作為絕頂聰明的人,必然會在兩種決策中選擇最優的。所以返回max

然後來分析s(i,j):

1.如果i==j,後拿紙牌的人什麼也拿不到,返回0

2.如果i!=j,玩家的對手會先拿紙牌。對手要麼先拿走a[i],要麼先拿走arr[j],如果對手拿走arr[i],那麼排列剩下arr[i+1,j],如果對手拿走arr[j],剩下arr[i,j-1],對手也是絕頂聰明的人,所以也會把最差的情況留給玩家因此返回min

解法一:public int cardgame(int arr, int n)

return math.max(f(arr,0,n-1),s(arr,0,n-1)); // write code here

}public int f(intarr,int start,int end)

return math.max(arr[start]+s(arr,start+1,end),arr[end]+s(arr,start,end-1));

}public int s(intarr,int start,int end)

return math.min(f(arr,start+1,end),f(arr,start,end-1));

}解法二:public int cardgame(int arr, int n)

int f=new int[n][n];

int s=new int[n][n];

for(int j=0;jf[j][j]=arr[j];

for(int i=j-1;i>=0;i--)

}return math.max(f[0][n-1], s[0][n-1]);}

第乙個玩家每次會保證自己是本次和另乙個玩家取完最大值後再取剩下的的和最大f[i][j]就記錄最大值即math.max(arr[j]+

s[i][j-1]

,arr[i]+

s[i+1][j]);

對於另乙個玩家來說,會取第乙個玩家取完剩下的較大的數,對於第乙個玩家來說就是沒取數之前較小的即math.min(f[i+1][j],f[i][j-1]);

換錢的方法數:

給定陣列arr,arr中所有的值都為正數且不重複,每個值代表一種面值的貨幣,每種面值的貨幣可以使用任意張,在給定乙個陣列aim代表要找的錢數,求換錢有多少種方法

暴力遞迴的方法。arr[5,10,25,1],aim=1000,分析過程如下:

1.用0張5元的,讓其餘的組成1000,

2.用1張5元的,其餘的組成995

3.用2張5.。。

其餘的又用0張,一張。。。將所有的張數加起來就是最後的結果。

解法一:暴力遞迴法:

public int coins(int arr,int aim)

return proccess(arr,0,aim);

}public int process(int arr,int index,int aim)

else

}return res;

}解法二:記憶化搜尋:

用乙個map陣列儲存上一次計算出的中間值,map[i][j]表示遞迴過程p(i,j)的返回值。

map[i][j]=0,表示遞迴過程未計算過,map[i][j]=-1表示遞迴過程計算過,但結果為0,其他情況時則儲存遞迴過程的值。

public int coins(int arr,int aim)

int map=new int[arr.length+1][aim.length+1];

return process(arr,0,aim,map);

}public int process(int arr,int index,int aim,int map)

else

else}}

map[index][aim]=res==0?-1:res;

return res;

}解法三:生成行數n,列數aim+1的矩陣dp,dp[i][j]的含義是在使用arr[0...i]的貨幣的情況下,組成錢數j有多少種方法。

1.對於矩陣dp的第一列dp[...][0],表示組成錢數為0的方法數,一種,很明顯就是不使用任何貨幣,所以dp的值為1.

2.對於矩陣dp的第一行的值dp[0][...],表示只能使用arr[0]的情況下,組成錢的方法數dp[i][k*arr[0]]=1

3.除第一行和第一列的其他位置,dp[i][j]是以下幾個情況的累加和

完全不用arr[i]貨幣,剩下的錢用arr[0..i]組成時,方法數為dp[i-1][j]

用乙個arr[i]貨幣,剩下的錢用arr[0..i]組成,方法數為dp[i-1][j-arr[j]*i]

最終dp[i][j]就是最終結果。

public int coins(int arr,int aim)

int dp=new int[arr.length][aim+1];

for(int i=0;ifor(int j=1;jfor(int i=1;ifor(int j=1;j<=aim;j++)

}return dp[arr.length-1][aim];

}以上方法的時間複雜度o(n*aim),空間複雜度o(aim);

再將空間壓縮,得到空間複雜度o(aim)的演算法

public int coins(int arr,int aim)

int dp=new int[aim+1];

for(int i=0;arr[0]*j<=aim;j++)

for(int i=1;ifor(int j=1;j<=aim;j++)

}return dp[aim];

}跳躍遊戲

給定陣列arr,arr[i]==k,代表從位置i向右跳躍1~k個距離。比如arr[2]==3,代表從位置2可以跳到位置3,位置4,位置5。如果從位置0出發,返回最少跳幾次能夠跳到arr最後的位置上。

從左到右遍歷arr,假設遍歷到位置i,

如果cur大於=i,說明條jump步可以到達位置i,

如果cur小於i,說明不能夠到大位置,需要多跳一步才行

public jump(int arr)

int jump=0;

int cur=0;

int next=0;

for(int i=0;iif(curnext=math.max(next,i+arr[i]);//將next更新,表示下一跳多跳一步到達的最遠位置

}return jump;

}

動態規劃 排成一條線的紙牌博弈問題

題目 給定乙個整型陣列arr,代表數值不同的紙牌排成一條線。玩家a和玩家b依次拿走每張紙牌,規定玩家a先拿,玩家b後拿,但是每個玩家每次只能拿走最左或最右的紙牌,玩家a和玩家b都絕頂聰明。請返回最後獲勝者的分數。舉例 arr 1,2,100,4 開始時玩家a只能拿走1或4。如果玩家a拿走1,則排列變...

排成一條線的紙牌博弈問題

問題描述 給定乙個整型陣列,代表數值不同的紙牌排成一條線。玩家a和玩家b依次拿走每張紙牌,規定玩家a先拿,玩家b後拿。但是每個玩家每次只能拿走最左或者最右的紙牌,玩家a和b都絕頂聰明,請返回最後獲勝者的分數。解答 定義乙個函式 func int arr 用來表示最優結果,那麼針對第乙個人是先拿第一張...

排成一條線的硬幣博弈問題

面值為正數的硬幣放置成一排,玩家1和玩家2輪流拿走硬幣,規定每個玩家在拿硬幣時,只能拿走最左或最右的硬幣。例如 硬幣面值與排列為 1,2,3,4,5,現在輪到玩家1拿硬幣。在當前狀態下,玩家1只能拿走1或5,如果玩家1拿走1,則排列變為2,3,4,5,那麼接下來玩家2可以拿走2或5,然後繼續輪到玩家...