一、LeetCode 877 石子游戏
题目描述:
亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] 。
游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。
亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。
假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true ,当李赢得比赛时返回 false 。
思路:
首先找到状态转移方程:
dp[i][j]=max(piles[i]-dp[i+1][j],piles[j]-dp[i][j-1]);
表示:一堆从 i 到 j 的石子
当先手拿 i ,那么后手从 i+1 到 j 中拿(最优的方法拿)
当先手拿 j ,那么后手从 i 到 j-1 中拿(最优的方法拿)
代码如下:
class Solution {
public:
bool stoneGame(vector<int>& piles) {
int n=piles.size();
vector<vector<int>>dp(n,vector<int>(n,0));
for(int i=0;i<n;i++){
dp[i][i]=piles[i];
}
for(int d=1;d<n;d++){//计算两个石子......
for(int j=0;j<n-d;j++){
dp[j][j+d]=max(piles[j]-dp[j+1][j+d],piles[d+j]-dp[j][j+d-1]);
}
}
return dp[0][n-1]>0;
}
};
二、LeetCode 1140 石子游戏II
题目描述:
亚历克斯和李继续他们的石子游戏。许多堆石子 排成一行,每堆都有正整数颗石子 piles[i]。游戏以谁手中的石子最多来决出胜负。
亚历克斯和李轮流进行,亚历克斯先开始。最初,M = 1。
在每个玩家的回合中,该玩家可以拿走剩下的 前 X 堆的所有石子,其中 1 <= X <= 2M。然后,令 M = max(M, X)。
游戏一直持续到所有石子都被拿走。
假设亚历克斯和李都发挥出最佳水平,返回亚历克斯可以得到的最大数量的石头。
代码如下:
class Solution {
public:
int stoneGameII(vector<int>& piles) {
int n=piles.size();
int sum=0;
vector<vector<int>>dp(n,vector<int>(n+1,0));
for(int i=n-1;i>=0;i--){
sum+=piles[i];
for(int M=1;M<=n;M++){
if(i+2*M>=n){
dp[i][M]=sum;
}
else{
for(int x=1;x<=2*M;x++){
dp[i][M]=max(dp[i][M],sum-dp[i+x][max(M,x)]);
}
}
}
}
return dp[0][1];
}
};
三、LeetCode 1406 石子游戏III
题目描述:
Alice 和 Bob 用几堆石子在做游戏。几堆石子排成一行,每堆石子都对应一个得分,由数组 stoneValue 给出。
Alice 和 Bob 轮流取石子,Alice 总是先开始。在每个玩家的回合中,该玩家可以拿走剩下石子中的的前 1、2 或 3 堆石子 。比赛一直持续到所有石头都被拿走。
每个玩家的最终得分为他所拿到的每堆石子的对应得分之和。每个玩家的初始分数都是 0 。比赛的目标是决出最高分,得分最高的选手将会赢得比赛,比赛也可能会出现平局。
假设 Alice 和 Bob 都采取 最优策略 。如果 Alice 赢了就返回 “Alice” ,Bob 赢了就返回 “Bob”,平局(分数相同)返回 “Tie” 。
代码如下:
class Solution {
public:
string stoneGameIII(vector<int>& stoneValue) {
int n=stoneValue.size();
vector<int>sum(n+1,0);
vector<int>dp(n+1,INT_MIN);
for(int i=n-1;i>=0;i--){
sum[i]=sum[i+1]+stoneValue[i];
}
dp[n]=0;
for(int i=n-1;i>=0;i--){
// int cur=0;
for(int j=i;j<n&&j<=i+2;j++){
// cur+=stoneValue[j];
dp[i]=max(dp[i],sum[i]-dp[j+1]);
}
}
if(dp[0]==sum[0]-dp[0]) return "Tie";
if(dp[0]>sum[0]-dp[0]) return "Alice";
return "Bob";
}
};
四、LeetCode 1510 石子游戏IV
题目描述:
Alice 和 Bob 两个人轮流玩一个游戏,Alice 先手。
一开始,有 n 个石子堆在一起。每个人轮流操作,正在操作的玩家可以从石子堆里拿走 任意 非零 平方数 个石子。
如果石子堆里没有石子了,则无法操作的玩家输掉游戏。
给你正整数 n ,且已知两个人都采取最优策略。如果 Alice 会赢得比赛,那么返回 True ,否则返回 False 。
代码如下:
class Solution {
public:
bool winnerSquareGame(int n) {
vector<bool>state(n+1,false);
for(int i=1;i<=n;i++){
int s=sqrt(i);
if(s*s==i){
state[i]=true;
}
else{
for(int j=1;j*j<i;j++){
if(state[i-j*j]==false){
state[i]=true;
break;
}
}
}
}
return state[n];
}
};
五、LeetCode 1563 石子游戏V
题目描述:
几块石子 排成一行 ,每块石子都有一个关联值,关联值为整数,由数组 stoneValue 给出。
游戏中的每一轮:Alice 会将这行石子分成两个 非空行(即,左侧行和右侧行);Bob 负责计算每一行的值,即此行中所有石子的值的总和。Bob 会丢弃值最大的行,Alice 的得分为剩下那行的值(每轮累加)。如果两行的值相等,Bob 让 Alice 决定丢弃哪一行。下一轮从剩下的那一行开始。
只 剩下一块石子 时,游戏结束。Alice 的分数最初为 0 。
返回 Alice 能够获得的最大分数 。
思路:
dfs+记忆
(时间复杂度 n^3)
代码如下:
class Solution {
vector<vector<int>>f;
public:
int dfs(vector<int>&nums,int left,int right){
if(left==right){
return 0;
}
if(f[left][right]){
return f[left][right];
}
int sum=0;
for(int i=left;i<=right;i++){
sum+=nums[i];
}
int sum_left=0;
for(int i=left;i<right;i++){
sum_left+=nums[i];
int sum_right=sum-sum_left;
if(sum_left>sum_right){
f[left][right]=max(f[left][right],dfs(nums,i+1,right)+sum_right);
}
else if(sum_left<sum_right){
f[left][right]=max(f[left][right],dfs(nums,left,i)+sum_left);
}
else{
f[left][right]=max(f[left][right],max(dfs(nums,left,i),dfs(nums,i+1,right))+sum_left);
}
}
return f[left][right];
}
int stoneGameV(vector<int>& stoneValue) {
int n=stoneValue.size();
vector<int>temp(n,0);
for(int i=0;i<n;i++){
f.push_back(temp);
}
return dfs(stoneValue,0,n-1);
}
};
六、LeetCode 1686 石子游戏VI
题目描述:
Alice 和 Bob 轮流玩一个游戏,Alice 先手。
一堆石子里总共有 n 个石子,轮到某个玩家时,他可以 移出 一个石子并得到这个石子的价值。Alice 和 Bob 对石子价值有 不一样的的评判标准 。双方都知道对方的评判标准。
给你两个长度为 n 的整数数组 aliceValues 和 bobValues 。aliceValues[i] 和 bobValues[i] 分别表示 Alice 和 Bob 认为第 i 个石子的价值。
所有石子都被取完后,得分较高的人为胜者。如果两个玩家得分相同,那么为平局。两位玩家都会采用 最优策略 进行游戏。
请你推断游戏的结果,用如下的方式表示:
如果 Alice 赢,返回 1 。
如果 Bob 赢,返回 -1 。
如果游戏平局,返回 0 。
思路:
要不自己获得最大的价值,
要不自己获得最小的价值,让对手不能获得最大的价值
代码如下:
class Solution {
public:
int stoneGameVI(vector<int>& aliceValues, vector<int>& bobValues) {
vector<pair<int,int>>value;
for(int i=0;i<aliceValues.size();i++){
value.emplace_back(aliceValues[i]+bobValues[i],i);
}
sort(value.rbegin(),value.rend());//降序
int sum_alice=0;
int sum_bob=0;
for(int i=0;i<aliceValues.size();i++){
if(i%2==0){
sum_alice+=aliceValues[value[i].second];
}
else{
sum_bob+=bobValues[value[i].second];
}
}
if(sum_alice>sum_bob) return 1;
else if(sum_alice<sum_bob) return -1;
return 0;
}
};
七、LeetCode 1690 石子游戏VII
题目描述:
石子游戏中,爱丽丝和鲍勃轮流进行自己的回合,爱丽丝先开始 。
有 n 块石子排成一排。每个玩家的回合中,可以从行中 移除 最左边的石头或最右边的石头,并获得与该行中剩余石头值之 和 相等的得分。当没有石头可移除时,得分较高者获胜。
鲍勃发现他总是输掉游戏(可怜的鲍勃,他总是输),所以他决定尽力 减小得分的差值 。爱丽丝的目标是最大限度地 扩大得分的差值 。
给你一个整数数组 stones ,其中 stones[i] 表示 从左边开始 的第 i 个石头的值,如果爱丽丝和鲍勃都 发挥出最佳水平 ,请返回他们 得分的差值 。
思路:
找到状态转移方程
dp[i][j]=max(sum[i+1][j]-dp[i+1][j],sum[i][j-1]-dp[i][j-1])
因此首先需要计算区间和
然后动态规划
代码如下:
class Solution {
public:
int stoneGameVII(vector<int>& stones) {
int n=stones.size();
vector<vector<int>>sum(n,vector<int>(n));
vector<vector<int>>dp(n,vector<int>(n));
for(int i=0;i<n;i++){
for(int j=i;j<n;j++){
if(i==j) sum[i][i]=stones[i];
else{
sum[i][j]=sum[i][j-1]+stones[j];
}
}
}
for(int i=n-1;i>=0;i--){
for(int j=i;j<n;j++){
if(i==j) dp[i][j]=0;
else dp[i][j]=max(sum[i+1][j]-dp[i+1][j],sum[i][j-1]-dp[i][j-1]);
}
}
return dp[0][n-1];
}
};