leetcode-master/problems/0649.Dota2参议院.md

230 lines
9.3 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 649. Dota2 参议院
[力扣题目链接](https://leetcode-cn.com/problems/dota2-senate/)
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 阵营的参议员可以使用他的第一项权利禁止第一个参议员的权利,
因此在第二轮只剩下第三个参议员拥有投票的权利,于是他可以宣布胜利。
# 思路
这道题 题意太绕了,我举一个更形象的例子给大家捋顺一下。
例如输入"RRDDD",执行过程应该是什么样呢?
* 第一轮senate[0]的R消灭senate[2]的Dsenate[1]的R消灭senate[3]的Dsenate[4]的D消灭senate[0]的R此时剩下"RD",第一轮结束!
* 第二轮senate[0]的R消灭senate[1]的D第二轮结束
* 第三轮只有R了R胜利
估计不少同学都困惑R和D数量相同怎么办究竟谁赢**其实这是一个持续消灭的过程!** 即如果同时存在R和D就继续进行下一轮消灭轮数直到只剩下R或者D为止
那么每一轮消灭的策略应该是什么呢?
例如RDDRD
第一轮senate[0]的R消灭senate[1]的D那么senate[2]的D是消灭senate[0]的R还是消灭senate[3]的R呢
当然是消灭senate[3]的R因为当轮到这个R的时候它可以消灭senate[4]的D。
**所以消灭的策略是,尽量消灭自己后面的对手,因为前面的对手已经使用过权利了,而后序的对手依然可以使用权利消灭自己的同伴!**
那么局部最优:有一次权利机会,就消灭自己后面的对手。全局最优:为自己的阵营赢取最大利益。
局部最优可以退出全局最优,举不出反例,那么试试贪心。
如果对贪心算法理论基础还不了解的话,可以看看这篇:[关于贪心算法,你该了解这些!](https://programmercarl.com/贪心算法理论基础.html) ,相信看完之后对贪心就有基本的了解了。
# 代码实现
实现代码,在每一轮循环的过程中,去过模拟优先消灭身后的对手,其实是比较麻烦的。
这里有一个技巧就是用一个变量记录当前参议员之前有几个敌对对手了进而判断自己是否被消灭了。这个变量我用flag来表示。
C++代码如下:
```CPP
class Solution {
public:
string predictPartyVictory(string senate) {
// R = true表示本轮循环结束后字符串里依然有R。D同理
bool R = true, D = true;
// 当flag大于0时R在D前出现R可以消灭D。当flag小于0时D在R前出现D可以消灭R
int flag = 0;
while (R && D) { // 一旦R或者D为false就结束循环说明本轮结束后只剩下R或者D了
R = false;
D = false;
for (int i = 0; i < senate.size(); i++) {
if (senate[i] == 'R') {
if (flag < 0) senate[i] = 0; // 消灭RR此时为false
else R = true; // 如果没被消灭本轮循环结束有R
flag++;
}
if (senate[i] == 'D') {
if (flag > 0) senate[i] = 0;
else D = true;
flag--;
}
}
}
// 循环结束之后R和D只能有一个为true
return R == true ? "Radiant" : "Dire";
}
};
```
# 其他语言版本
## Java
```java
class Solution {
public String predictPartyVictory(String senateStr) {
// R = true表示本轮循环结束后字符串里依然有R。D同理
Boolean R = true, D = true;
// 当flag大于0时R在D前出现R可以消灭D。当flag小于0时D在R前出现D可以消灭R
int flag = 0;
byte[] senate = senateStr.getBytes();
while (R && D) { // 一旦R或者D为false就结束循环说明本轮结束后只剩下R或者D了
R = false;
D = false;
for (int i = 0; i < senate.length; i++) {
if (senate[i] == 'R') {
if (flag < 0) senate[i] = 0; // 消灭RR此时为false
else R = true; // 如果没被消灭本轮循环结束有R
flag++;
}
if (senate[i] == 'D') {
if (flag > 0) senate[i] = 0;
else D = true;
flag--;
}
}
}
// 循环结束之后R和D只能有一个为true
return R == true ? "Radiant" : "Dire";
}
}
```
## Python
```python
class Solution:
def predictPartyVictory(self, senate: str) -> str:
# R = true表示本轮循环结束后字符串里依然有R。D同理
R , D = True, True
# 当flag大于0时R在D前出现R可以消灭D。当flag小于0时D在R前出现D可以消灭R
flag = 0
senate = list(senate)
while R and D: # 一旦R或者D为false就结束循环说明本轮结束后只剩下R或者D了
R = False
D = False
for i in range(len(senate)) :
if senate[i] == 'R' :
if flag < 0: senate[i] = '0' # 消灭RR此时为false
else: R = True # 如果没被消灭本轮循环结束有R
flag += 1
if senate[i] == 'D':
if flag > 0: senate[i] = '0'
else: D = True
flag -= 1
# 循环结束之后R和D只能有一个为true
return "Radiant" if R else "Dire"
```
## Go
```go
func predictPartyVictory(senateStr string) string {
// R = true表示本轮循环结束后字符串里依然有R。D同理
R, D := true, true
// 当flag大于0时R在D前出现R可以消灭D。当flag小于0时D在R前出现D可以消灭R
flag := 0
senate := []byte(senateStr)
for R && D { // 一旦R或者D为false就结束循环说明本轮结束后只剩下R或者D了
R = false
D = false
for i := 0; i < len(senate); i++ {
if senate[i] == 'R' {
if flag < 0 {
senate[i] = 0 // 消灭RR此时为false
} else {
R = true // 如果没被消灭本轮循环结束有R
}
flag++;
}
if (senate[i] == 'D') {
if flag > 0 {
senate[i] = 0
} else {
D = true
}
flag--
}
}
}
// 循环结束之后R和D只能有一个为true
if R {
return "Radiant"
}
return "Dire";
}
```
## JavaScript
```js
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>