【数据结构与算法】之深入解析“石子游戏VI”的求解思路与算法示例

一、题目要求

  • Alice 和 Bob 轮流玩一个游戏,Alice 先手,一堆石子里总共有 n 个石子,轮到某个玩家时,他可以移出一个石子并得到这个石子的价值。Alice 和 Bob 对石子价值有不一样的的评判标准,双方都知道对方的评判标准。
  • 给你两个长度为 n 的整数数组 aliceValues 和 bobValues,aliceValues[i] 和 bobValues[i] 分别表示 Alice 和 Bob 认为第 i 个石子的价值。
  • 所有石子都被取完后,得分较高的人为胜者,如果两个玩家得分相同,那么为平局,两位玩家都会采用 最优策略 进行游戏。
  • 请你推断游戏的结果,用如下的方式表示:
    • 如果 Alice 赢,返回 1;
    • 如果 Bob 赢,返回 -1;
    • 如果游戏平局,返回 0。
  • 示例 1:
输入:aliceValues = [1,3], bobValues = [2,1]
输出:1
解释:
如果 Alice 拿石子 1 (下标从 0开始),那么 Alice 可以得到 3 分。
Bob 只能选择石子 0 ,得到 2 分。
Alice 获胜。
  • 示例 2:
输入:aliceValues = [1,2], bobValues = [3,1]
输出:0
解释:
Alice 拿石子 0Bob 拿石子 1 ,他们得分都为 1 分。
打平。
  • 示例 3:
输入:aliceValues = [2,4,3], bobValues = [1,6,7]
输出:-1
解释:
不管 Alice 怎么操作,Bob 都可以得到比 Alice 更高的得分。
比方说,Alice 拿石子 1Bob 拿石子 2Alice 拿石子 0Alice 会得到 6 分而 Bob 得分为 7 分。
Bob 会获胜。

二、求解算法

① 贪心算法

  • 假设只有两个石头,对于 a, b 的价值分别是 a1,a2,b1,b2:
    • 第一种方案是 A 取第一个,B 取第二个,A 与 B 的价值差是 c1 = a1 - b2;
    • 第二种方案是 A 取第二个,B 取第一个,A 与 B 的价值差是 c2 = a2 - b1;
  • 那么这两种方案对于 A 来说哪一种更优,就取决于两个方案的价值差的比较。
  • 记 c = c1 - c2 = (a1 - b2) - (a2 - b1) = (a1 + b1) - (a2 + b2),如果 c > 0 那么方案一更优,如果 c == 0,那么两种方案价值一样,如果 c < 0 ,那么方案二更优。
  • 比较两个方案的优劣就如同比较 a1 + b1 与 a2 + b2 的优劣,归纳一下就是比较每个下标 i 的 a[i] + b[i] 的优劣,所以贪心的策略:将两组石头的价值合并,每次取价值最大的那一组。
  • 合并 aliceValues 与 bobValues 的数组,按最大价值从大到小排列,Alice 先选,Bob 后选,偶数下标就标称 Alice 选取的石头的总价值,Bob 选的则是奇数下标的石子,比较两个总和。
  • C++ 示例:
class Solution {
public:
    int stoneGameVI(vector<int>& aliceValues, vector<int>& bobValues) {
		vector<pair<int, int>> mp; // 记录价值和以及下标
		int n = aliceValues.size();
		for(int i = 0; i < n; i++) {
			int dis = aliceValues[i] + bobValues[i];
			mp.emplace_back(dis, i);
		}
		sort(mp.rbegin(), mp.rend()); // 从大到小排序
		int sum1 = 0, sum2 = 0;
		for(int i = 0; i < n; i++) {
			if(i % 2 == 0) sum1 += aliceValues[mp[i].second];// 偶数下标a来取
			else sum2 += bobValues[mp[i].second];  		  	 // 奇数下标b来取
		}
		if(sum1 == sum2) return 0; // 比较结果
		else if(sum1 > sum2) return 1;
		else return -1;
    }
};

② 数学推理

  • 双赢才是赢,不仅 Alice 要认为某块石头的价值大,Bob 也要认为的时候,才是真的大。
  • 因此,将某块石头在 Alice 眼中的价值 +Bob 眼里的价值相加,排序,然后 Alice 和 Bob 依次进行挑选,最比较 Alice 和 Bob 的得分。
  • C++ 示例:
class Solution {
public:
    int stoneGameVI(vector<int>& aliceValues, vector<int>& bobValues) {
        vector<pair<int,int>>vc;
        int len=aliceValues.size();
        for(int i=0;i<len;i++){
            // 合并石头的双从的价值
            int sum=aliceValues[i]+bobValues[i];
            vc.push_back({sum,i});
        }
        // 对合并后的石头的价值进行排序
        sort(vc.rbegin(),vc.rend());
        // Alice的得分
        int A=0;
        // Bob的得分
        int B=0;
        for(int i=0;i<len;i++){
            // 依次轮流拿去石头
            if(i%2==0) {
                A+=aliceValues[vc[i].second];
            } else {
                B+=bobValues[vc[i].second];
            }
        }
        // 返回最后的结果
        if(A==B){
            return 0;
        }
        return A>B?1:-1;
    }
};

三、博客之星

  • 今年是我第一次参加博客之星,需要各位大佬的支持,麻烦百忙之中,抽出一点宝贵的时间,给我投一下票:https://bbs.csdn.net/topics/603955258 给我一个五星(列表会一一全部回复),不胜感激!
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>