LeetCode每日一题--649. Dota2 参议院(贪心 队列)

题目:跳转至 649. Dota2 参议院
Dota2 的世界里有两个阵营:Radiant(天辉)和 Dire(夜魇)
Dota2 参议院由来自两派的参议员组成。现在参议院希望对一个 Dota2 游戏里的改变作出决定。他们以一个基于轮为过程的投票进行。在每一轮中,每一位参议员都可以行使两项权利中的一项:

  1. 禁止一名参议员的权利:
    参议员可以让另一位参议员在这一轮和随后的几轮中丧失所有的权利。
  2. 宣布胜利:
    (如果参议员发现有权利投票的参议员都是同一个阵营的,他可以宣布胜利并决定在游戏中的有关变化。)

给定一个字符串代表每个参议员的阵营。字母 “R” 和 “D” 分别代表了 Radiant(天辉)和 Dire(夜魇)。然后,如果有 n 个参议员,给定字符串的大小将是 n。

以轮为基础的过程从给定顺序的第一个参议员开始到最后一个参议员结束。这一过程将持续到投票结束。所有失去权利的参议员将在过程中被跳过。

假设每一位参议员都足够聪明,会为自己的政党做出最好的策略,你需要预测哪一方最终会宣布胜利并在 Dota2 游戏中决定改变。输出应该是 Radiant 或 Dire。

示例 1:
输入:“RD”
输出:“Radiant”
解释:第一个参议员来自 Radiant 阵营并且他可以使用第一项权利让第二个参议员失去权力,因此第二个参议员将被跳过因为他没有任何权利。然后在第二轮的时候,第一个参议员可以宣布胜利,因为他是唯一一个有投票权的人
示例 2:
输入:“RDD”
输出:“Dire”
解释:
第一轮中,第一个来自 Radiant 阵营的参议员可以使用第一项权利禁止第二个参议员的权利
第二个来自 Dire 阵营的参议员会被跳过因为他的权利被禁止
第三个来自 Dire 阵营的参议员可以使用他的第一项权利禁止第一个参议员的权利
因此在第二轮只剩下第三个参议员拥有投票的权利,于是他可以宣布胜利

提示:

  • 给定字符串的长度在 [1, 10,000] 之间.
class Solution {
public:
    string predictPartyVictory(string senate) {

    }
};

思路:
读完题目想到了一堆石头你拿一块我拿两块最后拿完的人胜利的那题,那好像还可以是个数学问题。
扯远了,回到这题,按轮来,同时最终条件是投票的都是同一阵营,那在没达到这个条件前,所有人的最优解都是选取权力一:禁止对方参议员的权利,而且是对方具有优先投票权的人(顺序在前)。
暴力模拟硬写出来的,如果一位议员被禁止,就改成X(闭嘴无人权),如果轮流到R,则他轮流到的第一个D变成X,反之亦然。一直循环直到R或者D一派全都变成X。

class Solution {
public:
    string predictPartyVictory(string senate) {
        int len=senate.length();
        if(len==1)
            return senate[0]=='R'?"Radiant":"Dire";
        int countR=0;
        for(auto x:senate)
            if(x=='R')
                ++countR;
        int countD=len-countR;
        int diffR=0,diffD=0;
        int i=0;  //注意越界
        while(countD!=diffD && countR!=diffR){  //如果仍存在R或者D
            if(senate[i]=='X'){
                ++i;
                if(i==len)
                    i=0;
                continue;
            }
            char s=senate[i]=='R'?'D':'R';
            int tmp=findNextSenate(senate,i,s);  //找寻下面第一个与自己党派不同的人
            senate[tmp]='X';
            if(s=='R')
                ++diffR;
            else
                ++diffD;
            ++i;
            if(i==len)
                i=0;
        }
        return diffR==countR?"Dire":"Radiant";
    }
    int findNextSenate(string senate,int index,char s){
        int len=senate.length();
        int i=0;
        while(i<len){
            int cur=(i+index)<len?i+index:i+index-len;  //按轮查找确保不越界,index必属于[0,len-1]
            if(senate[cur]!='X'){
                if(senate[cur]==s)
                    return cur;
            }
            ++i;
        }
        return -1;  //不存在
    }
};

看了题解,又想吹彩虹屁了(送自己一个X),利用队列记录下每位R和D出场的序号,两队的队首谁小谁就有发言权。如果被禁言,就pop再见,如果这轮存活,继续下一轮,不能穷追不舍不讲武德,所以序号加上总人数到下一轮再出场。直至最后还有人的那组就是赢家。

class Solution {
public:
    string predictPartyVictory(string senate) {
        int n = senate.size();
        queue<int> radiant, dire;
        for (int i = 0; i < n; ++i) {
            if (senate[i] == 'R') {
                radiant.push(i);
            }
            else {
                dire.push(i);
            }
        }
        while (!radiant.empty() && !dire.empty()) {
            if (radiant.front() < dire.front()) {
                radiant.push(radiant.front() + n);
            }
            else {
                dire.push(dire.front() + n);
            }
            radiant.pop();
            dire.pop();
        }
        return !radiant.empty() ? "Radiant" : "Dire";
    }
};

在注明标签的时候有点纠结,题解是循环,有点不得劲,举例选取最合适方案进行模拟,每次禁言都从第一个开始,局部最优,懂了,贪心。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值