0
点赞
收藏
分享

微信扫一扫

649. Dota2 参议院

草原小黄河 2022-03-11 阅读 72

地址:

力扣icon-default.png?t=M1L8https://leetcode-cn.com/problems/dota2-senate/

题目:

Dota2 的世界里有两个阵营:Radiant(天辉)和 Dire(夜魇)

Dota2 参议院由来自两派的参议员组成。现在参议院希望对一个 Dota2 游戏里的改变作出决定。他们以一个基于轮为过程的投票进行。在每一轮中,每一位参议员都可以行使两项权利中的一项:

禁止一名参议员的权利:

参议员可以让另一位参议员在这一轮和随后的几轮中丧失所有的权利。

宣布胜利:

          如果参议员发现有权利投票的参议员都是同一个阵营的,他可以宣布胜利并决定在游戏中的有关变化。

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

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

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

示例 1:


示例 2:

提示:

思路:

直接上示例:“RDRDD”

R对D禁言,D对R禁言,禁言意味这踢出,以 X 表示

1. R 对 D 禁言,变成 RXRDD

2. 第二个R(第一个D已经被第一个R变成了X),对第二个D禁言,变成了 RXRXD

3. D 对R禁言,变成了 XXRXD

4. R 对D 禁言,变成了 XXRXX,R胜利

考虑一个问题,R对D禁言(或者相反),应该禁言哪一个?

RDRDD or RDRDD or RDRDD

当然是最近的,不然最近的反面就可以很快发动禁言,这个可以自己推演

那么我们的思路就是禁言最近的反面

思路一样,写法有点不同,两种都需要处理多次循环的问题

方法一、贪心

void disablePerson(char *senate, int len, int start, char target)
{
    int i;

    if(start +1 == len) // last person, check from beginng
        start = -1;

    for(i=start+1; i<len; i++)
    {
        //printf("disable...target=%c, senate[%d]=%c\n", target, i, senate[i]);
        if(senate[i] == target)
        {
            senate[i] = 'X';
            break;
        }
    }

}

char * predictPartyVictory(char * senate){
    int len = strlen(senate);

    int i;
    int rNum = 0, dNum = 0;
    for(i=0; i<len; i++)
    {
        if(senate[i] == 'R')
            rNum++;
        else
            dNum++;
    }
    //printf("1.rNum=%d, dNum=%d\n", rNum, dNum);

    i = 0;
    while(rNum != 0 && dNum != 0)
    {
        //printf("loop.... senate[%d]=%c\n", i, senate[i]);
        
        if(senate[i] == 'R')
        {
            dNum--;
            disablePerson(senate, len, i, 'D');
        }
        else if(senate[i] == 'D')
        {
            rNum--;
            disablePerson(senate, len, i, 'R');
        }
        
        i++;
        if(i == len)
            i = 0;
    }

    // for(i=0; i<len; i++)
    //     printf("2. senate[%d]=%c\n", i, senate[i]);
    // printf("2.rNum=%d, dNum=%d\n", rNum, dNum);

    
    return rNum > 0 ? "Radiant" : "Dire";
}

方法二、贪心+队列

用两个队列分别存放 R,D

分别出队比较序号,序号小的可以禁言(不用实现,因为都出队了),然后再入队(到队末相当于第二轮),这里再入队的序号要加上一个offset,避免永久小(自己思考哈)

#define QUEUELEN 100000
 
int enqueue(int* queue, int len, int *rear, int index)
{
	if(*rear == len) // fulll
		return -1;
    
	queue[*rear] = index;
	(*rear)++;
 
	return 0;
}
 
int dequeue(int* queue, int len, int *front, int *rear)
{
	if(*front == *rear) // empty
		return -1;
	
	int idx = *front;
	if(idx == len - 1)
	{
		*front = 0;
		*rear = 0;
	}
	else
		(*front)++;

	return queue[idx];
}
 
int isEmpty(int front, int rear)
{
	return front == rear ? 1:0;
}
 
char * predictPartyVictory(char * senate){
    int i;
    int len = strlen(senate);

    int queueR[QUEUELEN], queueD[QUEUELEN];
    int frontR = 0, frontD = 0;
    int rearR = 0, rearD = 0;

    for(i=0; i<len; i++)
    {
        if(senate[i] == 'R')
            enqueue(queueR, QUEUELEN, &rearR, i);
        else
            enqueue(queueD, QUEUELEN, &rearD, i);
    }

    while(!isEmpty(frontR, rearR) && !isEmpty(frontD, rearD))
    {
        int rIdx = dequeue(queueR, QUEUELEN, &frontR, &rearR);
        int dIdx = dequeue(queueD, QUEUELEN, &frontD, &rearD);
        
        if(rIdx < dIdx)
            enqueue(queueR, QUEUELEN, &rearR, rIdx+len);
        else
            enqueue(queueD, QUEUELEN, &rearD, dIdx+len);
    }

    return !isEmpty(frontR, rearR) ? "Radiant" : "Dire";
}

查看更多刷题笔记

举报

相关推荐

0 条评论